blueink-client-python 1.0.0__tar.gz → 1.2.0__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 (37) hide show
  1. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/PKG-INFO +54 -3
  2. blueink-client-python-1.0.0/src/blueink_client_python.egg-info/PKG-INFO → blueink_client_python-1.2.0/README.md +50 -27
  3. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/setup.cfg +2 -2
  4. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/bundle_helper.py +233 -0
  5. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/client.py +14 -4
  6. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/endpoints.py +9 -0
  7. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/bundles.py +164 -0
  8. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/request_helper.py +9 -1
  9. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/bundle.py +101 -0
  10. blueink-client-python-1.0.0/src/blueink/subclients/template.py → blueink_client_python-1.2.0/src/blueink/subclients/envelope_template.py +17 -14
  11. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/person.py +1 -1
  12. blueink_client_python-1.2.0/src/blueink/subclients/template.py +99 -0
  13. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/webhook.py +0 -3
  14. blueink_client_python-1.2.0/src/blueink/tests/test_bundle_helper.py +634 -0
  15. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/tests/test_client.py +58 -0
  16. blueink-client-python-1.0.0/README.md → blueink_client_python-1.2.0/src/blueink_client_python.egg-info/PKG-INFO +78 -0
  17. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/SOURCES.txt +1 -0
  18. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/requires.txt +1 -1
  19. blueink-client-python-1.0.0/src/blueink/tests/test_bundle_helper.py +0 -188
  20. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/LICENSE +0 -0
  21. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/pyproject.toml +0 -0
  22. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/__init__.py +0 -0
  23. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/constants.py +0 -0
  24. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/__init__.py +0 -0
  25. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/persons.py +0 -0
  26. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/webhook.py +0 -0
  27. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/paginator.py +0 -0
  28. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/person_helper.py +0 -0
  29. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/__init__.py +0 -0
  30. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/packet.py +0 -0
  31. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/subclient.py +0 -0
  32. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/tests/__init__.py +0 -0
  33. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/tests/test_person_helper.py +0 -0
  34. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/utils/__init__.py +0 -0
  35. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/utils/testcase.py +0 -0
  36. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/dependency_links.txt +0 -0
  37. {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: blueink-client-python
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Python Client for Blueink eSignature API
5
5
  Home-page: https://github.com/blueinkhq/blueink-client-python
6
6
  Author: Blueink
@@ -19,11 +19,12 @@ Requires-Dist: email-validator
19
19
  Provides-Extra: munch
20
20
  Requires-Dist: munch>=2.5; extra == "munch"
21
21
  Provides-Extra: requests
22
- Requires-Dist: requests>=2.27; extra == "requests"
22
+ Requires-Dist: requests>=2.31; extra == "requests"
23
23
  Provides-Extra: pydantic
24
24
  Requires-Dist: pydantic>=1.9; extra == "pydantic"
25
25
  Provides-Extra: email-validator
26
26
  Requires-Dist: 1.2; extra == "email-validator"
27
+ Dynamic: license-file
27
28
 
28
29
  # blueink-client-python
29
30
  ![Tests](https://github.com/blueinkhq/blueink-client-python/actions/workflows/helper-tests.yml/badge.svg)
@@ -400,6 +401,56 @@ with open("/path/to/file/example.pdf", 'rb') as file:
400
401
  doc04_key = bh.add_document_by_file(file)
401
402
  ```
402
403
 
404
+ #### Auto-Placement Fields
405
+
406
+ Auto-placement fields allow you to automatically search for text in documents and place
407
+ signature/input fields at those locations with optional offsets. This eliminates the need
408
+ to manually specify exact coordinates for fields.
409
+
410
+ ```python
411
+ from blueink import BundleHelper, Client
412
+
413
+ bh = BundleHelper(
414
+ label="Auto-Placement Example",
415
+ email_subject="Please sign",
416
+ is_test=True,
417
+ )
418
+
419
+ signer_key = bh.add_signer(name="John Doe", email="john@example.com")
420
+ doc_key = bh.add_document_by_url("https://www.irs.gov/pub/irs-pdf/fw9.pdf")
421
+
422
+ # Add auto-placement field that searches for "Signature" text
423
+ bh.add_auto_placement(
424
+ document_key=doc_key,
425
+ kind="sig", # Field type: signature
426
+ search="Signature", # Text to search for
427
+ w=20, # Width
428
+ h=5, # Height
429
+ offset_x=-5, # Move 5 units left from found text
430
+ offset_y=2, # Move 2 units down from found text
431
+ editors=[signer_key],
432
+ )
433
+
434
+ # Add auto-placement for an input field
435
+ bh.add_auto_placement(
436
+ document_key=doc_key,
437
+ kind="inp", # Field type: input
438
+ search="Address", # Text to search for
439
+ w=20,
440
+ h=2,
441
+ offset_x=8, # Move 8 units right from found text
442
+ editors=[signer_key],
443
+ )
444
+
445
+ client = Client()
446
+ response = client.bundles.create_from_bundle_helper(bh)
447
+ ```
448
+
449
+ **Key benefits of auto-placement:**
450
+ - No need to manually find exact coordinates
451
+ - Works with template documents that have consistent text labels
452
+ - Automatically adjusts to text position in the document
453
+ - Can be combined with regular manually-positioned fields
403
454
 
404
455
  #### Retrieval
405
456
 
@@ -1,30 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: blueink-client-python
3
- Version: 1.0.0
4
- Summary: Python Client for Blueink eSignature API
5
- Home-page: https://github.com/blueinkhq/blueink-client-python
6
- Author: Blueink
7
- Author-email: pypi@blueink.com
8
- Project-URL: Bug Tracker, https://github.com/blueinkhq/blueink-client-python/issues
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.8
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Requires-Dist: munch
16
- Requires-Dist: requests
17
- Requires-Dist: pydantic
18
- Requires-Dist: email-validator
19
- Provides-Extra: munch
20
- Requires-Dist: munch>=2.5; extra == "munch"
21
- Provides-Extra: requests
22
- Requires-Dist: requests>=2.27; extra == "requests"
23
- Provides-Extra: pydantic
24
- Requires-Dist: pydantic>=1.9; extra == "pydantic"
25
- Provides-Extra: email-validator
26
- Requires-Dist: 1.2; extra == "email-validator"
27
-
28
1
  # blueink-client-python
29
2
  ![Tests](https://github.com/blueinkhq/blueink-client-python/actions/workflows/helper-tests.yml/badge.svg)
30
3
  ![Style Checks](https://github.com/blueinkhq/blueink-client-python/actions/workflows/style-checks.yml/badge.svg)
@@ -400,6 +373,56 @@ with open("/path/to/file/example.pdf", 'rb') as file:
400
373
  doc04_key = bh.add_document_by_file(file)
401
374
  ```
402
375
 
376
+ #### Auto-Placement Fields
377
+
378
+ Auto-placement fields allow you to automatically search for text in documents and place
379
+ signature/input fields at those locations with optional offsets. This eliminates the need
380
+ to manually specify exact coordinates for fields.
381
+
382
+ ```python
383
+ from blueink import BundleHelper, Client
384
+
385
+ bh = BundleHelper(
386
+ label="Auto-Placement Example",
387
+ email_subject="Please sign",
388
+ is_test=True,
389
+ )
390
+
391
+ signer_key = bh.add_signer(name="John Doe", email="john@example.com")
392
+ doc_key = bh.add_document_by_url("https://www.irs.gov/pub/irs-pdf/fw9.pdf")
393
+
394
+ # Add auto-placement field that searches for "Signature" text
395
+ bh.add_auto_placement(
396
+ document_key=doc_key,
397
+ kind="sig", # Field type: signature
398
+ search="Signature", # Text to search for
399
+ w=20, # Width
400
+ h=5, # Height
401
+ offset_x=-5, # Move 5 units left from found text
402
+ offset_y=2, # Move 2 units down from found text
403
+ editors=[signer_key],
404
+ )
405
+
406
+ # Add auto-placement for an input field
407
+ bh.add_auto_placement(
408
+ document_key=doc_key,
409
+ kind="inp", # Field type: input
410
+ search="Address", # Text to search for
411
+ w=20,
412
+ h=2,
413
+ offset_x=8, # Move 8 units right from found text
414
+ editors=[signer_key],
415
+ )
416
+
417
+ client = Client()
418
+ response = client.bundles.create_from_bundle_helper(bh)
419
+ ```
420
+
421
+ **Key benefits of auto-placement:**
422
+ - No need to manually find exact coordinates
423
+ - Works with template documents that have consistent text labels
424
+ - Automatically adjusts to text position in the document
425
+ - Can be combined with regular manually-positioned fields
403
426
 
404
427
  #### Retrieval
405
428
 
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = blueink-client-python
3
- version = 1.0.0
3
+ version = 1.2.0
4
4
  author = Blueink
5
5
  author_email = pypi@blueink.com
6
6
  description = Python Client for Blueink eSignature API
@@ -27,7 +27,7 @@ install_requires =
27
27
 
28
28
  [options.extras_require]
29
29
  munch = munch>=2.5;
30
- requests = requests>=2.27;
30
+ requests = requests>=2.31;
31
31
  pydantic = pydantic>=1.9
32
32
  email-validator> = 1.2
33
33
 
@@ -4,9 +4,13 @@ from os.path import basename
4
4
  from typing import List
5
5
 
6
6
  from blueink.model.bundles import (
7
+ AutoPlacement,
7
8
  Bundle,
8
9
  Document,
10
+ EnvelopeTemplate,
11
+ EnvelopeTemplateFieldValue,
9
12
  Field,
13
+ ImportedDocument,
10
14
  Packet,
11
15
  TemplateRef,
12
16
  TemplateRefAssignment,
@@ -25,6 +29,9 @@ class BundleHelper:
25
29
  is_test: bool = False,
26
30
  custom_key: str = None,
27
31
  team: str = None,
32
+ signing_brand: str = None,
33
+ expires: str = None,
34
+ tag_values: dict = None,
28
35
  ):
29
36
  """Helper class to aid building a Bundle.
30
37
 
@@ -38,6 +45,9 @@ class BundleHelper:
38
45
  is_test:
39
46
  custom_key:
40
47
  team:
48
+ signing_brand:
49
+ expires:
50
+ tag_values: Optional dict mapping data flow tags to pre-filled values
41
51
  """
42
52
  self._label = label
43
53
  self._in_order = in_order
@@ -49,6 +59,10 @@ class BundleHelper:
49
59
  self._packets = {}
50
60
  self._custom_key = custom_key
51
61
  self._team = team
62
+ self._signing_brand = signing_brand
63
+ self._expires = expires
64
+ self._tag_values = tag_values
65
+ self._envelope_template = None
52
66
 
53
67
  # for file uploads, index should match those in the document "file_index" field
54
68
  self.file_names = []
@@ -107,6 +121,20 @@ class BundleHelper:
107
121
 
108
122
  return self.add_document_by_b64(filename, b64str, **additional_data)
109
123
 
124
+ def add_document_by_html(self, html_content: str, **additional_data) -> str:
125
+ """Add a document using an HTML string for HTML-to-PDF conversion.
126
+
127
+ Args:
128
+ html_content: HTML content string
129
+ additional_data: Optional additional kwargs (e.g., filename, html_fields_mode)
130
+
131
+ Returns:
132
+ Document Key
133
+ """
134
+ document = Document.create(file_html=html_content, **additional_data)
135
+ self._documents[document.key] = document
136
+ return document.key
137
+
110
138
  def add_document_by_b64(self, filename: str, b64str: str, **additional_data):
111
139
  """Add a file using a b64 string; utf-8 encoded
112
140
 
@@ -205,6 +233,10 @@ class BundleHelper:
205
233
  v_pattern: str = None,
206
234
  v_min: int = None,
207
235
  v_max: int = None,
236
+ v_regex: str = None,
237
+ v_regex_msg: str = None,
238
+ v_attachment_types: List[str] = None,
239
+ data_flow_tag: str = None,
208
240
  key=None,
209
241
  **additional_data,
210
242
  ):
@@ -222,6 +254,10 @@ class BundleHelper:
222
254
  v_pattern: Optional
223
255
  v_min: Optional
224
256
  v_max: Optional
257
+ v_regex: Optional regular expression for field validation
258
+ v_regex_msg: Optional error message shown when v_regex validation fails
259
+ v_attachment_types: Optional list of allowed attachment file extensions (only for kind='att')
260
+ data_flow_tag: Optional data flow tag to associate with the field
225
261
  editors: Optional
226
262
  key: Optional
227
263
  additional_data: Optional and will append any additional kwargs to the json of the field
@@ -243,6 +279,10 @@ class BundleHelper:
243
279
  v_pattern=v_pattern,
244
280
  v_min=v_min,
245
281
  v_max=v_max,
282
+ v_regex=v_regex,
283
+ v_regex_msg=v_regex_msg,
284
+ v_attachment_types=v_attachment_types,
285
+ data_flow_tag=data_flow_tag,
246
286
  key=key,
247
287
  **additional_data,
248
288
  )
@@ -252,6 +292,75 @@ class BundleHelper:
252
292
  self._documents[document_key].add_field(field)
253
293
  return field.key
254
294
 
295
+ def add_auto_placement(
296
+ self,
297
+ document_key: str,
298
+ kind: str,
299
+ search: str,
300
+ w: int,
301
+ h: int,
302
+ offset_x: int = 0,
303
+ offset_y: int = 0,
304
+ editors: List[str] = None,
305
+ page: int = None,
306
+ v_attachment_types: List[str] = None,
307
+ **additional_data,
308
+ ):
309
+ """Add an auto-placement field to a document.
310
+
311
+ Auto-placement fields automatically search for text in the document and place
312
+ the field at the found location with optional offsets.
313
+
314
+ Args:
315
+ document_key: Key of the document to add the auto-placement to
316
+ kind: Field type (e.g., 'sig' for signature, 'inp' for input, 'ini' for initials)
317
+ search: Text to search for in the document
318
+ w: Width of the field
319
+ h: Height of the field
320
+ offset_x: Horizontal offset from the search text (default: 0)
321
+ offset_y: Vertical offset from the search text (default: 0)
322
+ editors: List of signer keys who can edit this field
323
+ page: Optional page number to limit search to
324
+ v_attachment_types: Optional list of allowed attachment file extensions (only for kind='att')
325
+ additional_data: Optional additional kwargs to append to the auto-placement
326
+
327
+ Returns:
328
+ None (auto-placements don't have keys like regular fields)
329
+
330
+ Example:
331
+ # Add a signature field that searches for "Signature" text
332
+ bh.add_auto_placement(
333
+ document_key=doc_key,
334
+ kind='sig',
335
+ search='Signature',
336
+ w=20,
337
+ h=5,
338
+ offset_x=-5,
339
+ offset_y=2,
340
+ editors=['signer-1']
341
+ )
342
+ """
343
+ if document_key not in self._documents:
344
+ raise RuntimeError(f"No document found with key {document_key}!")
345
+
346
+ auto_placement = AutoPlacement.create(
347
+ kind=kind,
348
+ search=search,
349
+ w=w,
350
+ h=h,
351
+ offset_x=offset_x,
352
+ offset_y=offset_y,
353
+ page=page,
354
+ v_attachment_types=v_attachment_types,
355
+ **additional_data,
356
+ )
357
+
358
+ if editors:
359
+ for editor_key in editors:
360
+ auto_placement.add_editor(editor_key)
361
+
362
+ self._documents[document_key].add_auto_placement(auto_placement)
363
+
255
364
  def add_signer(
256
365
  self,
257
366
  name: str,
@@ -264,6 +373,8 @@ class BundleHelper:
264
373
  auth_id: bool = False,
265
374
  order: int = None,
266
375
  key=None,
376
+ requires_witness: bool = None,
377
+ witness_nominated_by: str = None,
267
378
  **additional_data,
268
379
  ):
269
380
  """Create and add a signer. With at least an email xor phone number.
@@ -279,6 +390,8 @@ class BundleHelper:
279
390
  auth_id: Optional
280
391
  deliver_via: Optional
281
392
  order: Optional
393
+ requires_witness: Optional
394
+ witness_nominated_by: Optional
282
395
  additional_data: Optional and will append any additional kwargs to the json of the signer
283
396
 
284
397
  Returns:
@@ -298,6 +411,8 @@ class BundleHelper:
298
411
  deliver_via=deliver_via,
299
412
  order=order,
300
413
  key=key,
414
+ requires_witness=requires_witness,
415
+ witness_nominated_by=witness_nominated_by,
301
416
  **additional_data,
302
417
  )
303
418
  self._packets[packet.key] = packet
@@ -347,6 +462,64 @@ class BundleHelper:
347
462
  field_val = TemplateRefFieldValue.create(key, value, **additional_data)
348
463
  self._documents[document_key].field_values.append(field_val)
349
464
 
465
+ def set_envelope_template(
466
+ self, template_id: str, field_values: dict = None, **additional_data
467
+ ):
468
+ """Set the envelope template for this bundle.
469
+
470
+ When using an envelope template, the template contains the complete envelope
471
+ configuration including documents, signers, and field assignments.
472
+
473
+ Args:
474
+ template_id: Envelope template ID (format: T-xxxxxxxxxxx)
475
+ field_values: Optional dict mapping field keys to initial values
476
+ additional_data: Optional additional kwargs to append to the envelope template
477
+
478
+ Example:
479
+ bh = BundleHelper(label="Contract", is_test=True)
480
+ bh.add_signer(name="John Doe", email="john@example.com", key="signer-1")
481
+ bh.set_envelope_template(
482
+ template_id="T-abc123",
483
+ field_values={"company_name": "ACME Corp"}
484
+ )
485
+ """
486
+ vals = []
487
+ if field_values:
488
+ for field_key, init_val in field_values.items():
489
+ fieldval = EnvelopeTemplateFieldValue.create(
490
+ key=field_key, initial_value=init_val
491
+ )
492
+ vals.append(fieldval)
493
+
494
+ self._envelope_template = EnvelopeTemplate.create(
495
+ template_id=template_id,
496
+ field_values=vals if vals else None,
497
+ **additional_data,
498
+ )
499
+
500
+ def add_envelope_template_field_value(
501
+ self, key: str, initial_value: str, **additional_data
502
+ ):
503
+ """Add a field value to the envelope template.
504
+
505
+ Args:
506
+ key: Field key in the envelope template
507
+ initial_value: Initial value for the field
508
+ additional_data: Optional additional kwargs
509
+
510
+ Raises:
511
+ RuntimeError: If no envelope template has been set
512
+ """
513
+ if self._envelope_template is None:
514
+ raise RuntimeError(
515
+ "No envelope template set. Call set_envelope_template() first."
516
+ )
517
+
518
+ field_val = EnvelopeTemplateFieldValue.create(
519
+ key=key, initial_value=initial_value, **additional_data
520
+ )
521
+ self._envelope_template.add_field_value(field_val)
522
+
350
523
  def _compile_bundle(self, **additional_data) -> Bundle:
351
524
  """
352
525
  Builds a Bundle object complete with all the packets (signers) and documents added through the course
@@ -368,6 +541,9 @@ class BundleHelper:
368
541
  cc_emails=self._cc_emails,
369
542
  custom_key=self._custom_key,
370
543
  team=self._team,
544
+ signing_brand=self._signing_brand,
545
+ expires=self._expires,
546
+ tag_values=self._tag_values,
371
547
  **additional_data,
372
548
  )
373
549
  return bundle_out
@@ -384,6 +560,63 @@ class BundleHelper:
384
560
  bundle = self._compile_bundle(**additional_data)
385
561
  return bundle.dict(exclude_unset=True, exclude_none=True)
386
562
 
563
+ def as_data_for_envelope_template(self, **additional_data):
564
+ """Return data for creating a bundle from an envelope template.
565
+
566
+ This method is used when creating a bundle from an envelope template.
567
+ It returns a dictionary with packets and envelope_template fields.
568
+
569
+ Args:
570
+ additional_data: extra data to append to the request, as a dict
571
+
572
+ Returns:
573
+ Dictionary suitable for create_from_envelope_template endpoint
574
+
575
+ Raises:
576
+ RuntimeError: If no envelope template has been set
577
+
578
+ Example:
579
+ bh = BundleHelper(label="Contract", is_test=True)
580
+ bh.add_signer(name="John Doe", email="john@example.com", key="signer-1")
581
+ bh.set_envelope_template("T-abc123", {"company_name": "ACME"})
582
+ data = bh.as_data_for_envelope_template()
583
+ """
584
+ if self._envelope_template is None:
585
+ raise RuntimeError(
586
+ "No envelope template set. Call set_envelope_template() first."
587
+ )
588
+
589
+ packets = list(self._packets.values())
590
+ result = {
591
+ "packets": [p.dict(exclude_unset=True, exclude_none=True) for p in packets],
592
+ "envelope_template": self._envelope_template.dict(
593
+ exclude_unset=True, exclude_none=True
594
+ ),
595
+ }
596
+
597
+ # Add optional bundle fields
598
+ if self._label:
599
+ result["label"] = self._label
600
+ if self._is_test is not None:
601
+ result["is_test"] = self._is_test
602
+ if self._email_subj:
603
+ result["email_subject"] = self._email_subj
604
+ if self._email_msg:
605
+ result["email_message"] = self._email_msg
606
+ if self._cc_emails:
607
+ result["cc_emails"] = self._cc_emails
608
+ if self._custom_key:
609
+ result["custom_key"] = self._custom_key
610
+ if self._team:
611
+ result["team"] = self._team
612
+ if self._signing_brand:
613
+ result["signing_brand"] = self._signing_brand
614
+
615
+ # Add any additional data
616
+ result.update(additional_data)
617
+
618
+ return result
619
+
387
620
  def as_json(self, **additional_data):
388
621
  """Return a Bundle as a json
389
622
 
@@ -7,6 +7,7 @@ from blueink.constants import (
7
7
  )
8
8
  from blueink.request_helper import RequestHelper
9
9
  from blueink.subclients.bundle import BundleSubClient
10
+ from blueink.subclients.envelope_template import EnvelopeTemplateSubClient
10
11
  from blueink.subclients.packet import PacketSubClient
11
12
  from blueink.subclients.person import PersonSubClient
12
13
  from blueink.subclients.template import TemplateSubClient
@@ -19,6 +20,7 @@ class Client:
19
20
  private_api_key: str = None,
20
21
  base_url: str = None,
21
22
  raise_exceptions: bool = True,
23
+ security_headers: dict = None,
22
24
  ):
23
25
  """Initialize a Client instance to access the Blueink eSignature API
24
26
 
@@ -29,6 +31,7 @@ class Client:
29
31
  base_url: override the API base URL. If not supplied, we check the
30
32
  environment variable BLUEINK_API_URL. If that is empty, the default
31
33
  value of "https://api.blueink.com/api/v2" is used.
34
+ security_headers: Place for additional security headers, likely unnecessary
32
35
  raise_exceptions (Default True): raise HTTPError if code != 200. Otherwise
33
36
  return as NormalizedResponse objects.
34
37
 
@@ -44,9 +47,9 @@ class Client:
44
47
 
45
48
  if not private_api_key:
46
49
  raise ValueError(
47
- "A Blueink Private API Key must be provided on Client initialization"
48
- " or specified via the environment variable"
49
- " {ENV_BLUEINK_PRIVATE_API_KEY}"
50
+ f"A Blueink Private API Key must be provided on Client initialization"
51
+ f" or specified via the environment variable"
52
+ f" {ENV_BLUEINK_PRIVATE_API_KEY}"
50
53
  )
51
54
 
52
55
  if not base_url:
@@ -57,10 +60,17 @@ class Client:
57
60
 
58
61
  self._base_url = base_url
59
62
 
60
- self._request_helper = RequestHelper(private_api_key, raise_exceptions)
63
+ self._request_helper = RequestHelper(
64
+ private_api_key,
65
+ raise_exceptions,
66
+ security_headers=security_headers,
67
+ )
61
68
 
62
69
  self.bundles = BundleSubClient(self._base_url, self._request_helper)
63
70
  self.persons = PersonSubClient(self._base_url, self._request_helper)
64
71
  self.packets = PacketSubClient(self._base_url, self._request_helper)
65
72
  self.templates = TemplateSubClient(self._base_url, self._request_helper)
73
+ self.envelope_templates = EnvelopeTemplateSubClient(
74
+ self._base_url, self._request_helper
75
+ )
66
76
  self.webhooks = WebhookSubClient(self._base_url, self._request_helper)
@@ -8,6 +8,8 @@ from string import Template
8
8
 
9
9
  class BUNDLES:
10
10
  CREATE = "/bundles/"
11
+ CREATE_FROM_ENVELOPE_TEMPLATE = "/bundles/create_from_envelope_template/"
12
+ CREATE_PREPARATION_SESSION = "/bundles/preparation_session/"
11
13
  LIST = "/bundles/"
12
14
  RETRIEVE = "/bundles/${bundle_id}/"
13
15
  CANCEL = "/bundles/${bundle_id}/cancel/"
@@ -34,6 +36,13 @@ class PACKETS:
34
36
  class TEMPLATES:
35
37
  LIST = "/templates/"
36
38
  RETRIEVE = "/templates/${template_id}/"
39
+ UPDATE = "/templates/${template_id}/"
40
+ CREATE_PREPARATION_SESSION = "/templates/preparation_session/"
41
+
42
+
43
+ class ENVELOPE_TEMPLATES:
44
+ LIST = "/envelope-templates/"
45
+ RETRIEVE = "/envelope-templates/${envelope_template_id}/"
37
46
 
38
47
 
39
48
  class WEBHOOKS: