blueink-client-python 0.9.3__tar.gz → 1.0.1__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 (42) hide show
  1. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/PKG-INFO +331 -48
  2. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/README.md +320 -45
  3. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/pyproject.toml +6 -0
  4. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/setup.cfg +9 -5
  5. blueink_client_python-1.0.1/src/blueink/__init__.py +8 -0
  6. blueink_client_python-1.0.1/src/blueink/bundle_helper.py +580 -0
  7. blueink_client_python-1.0.1/src/blueink/client.py +76 -0
  8. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/constants.py +10 -0
  9. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/endpoints.py +29 -0
  10. blueink_client_python-1.0.1/src/blueink/model/bundles.py +312 -0
  11. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/model/persons.py +2 -4
  12. blueink_client_python-1.0.1/src/blueink/model/webhook.py +30 -0
  13. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/paginator.py +6 -5
  14. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/person_helper.py +20 -9
  15. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/request_helper.py +28 -7
  16. blueink_client_python-1.0.1/src/blueink/subclients/__init__.py +0 -0
  17. blueink_client_python-1.0.1/src/blueink/subclients/bundle.py +322 -0
  18. blueink_client_python-1.0.1/src/blueink/subclients/envelope_template.py +64 -0
  19. blueink_client_python-1.0.1/src/blueink/subclients/packet.py +57 -0
  20. blueink_client_python-1.0.1/src/blueink/subclients/person.py +124 -0
  21. blueink_client_python-1.0.1/src/blueink/subclients/subclient.py +52 -0
  22. blueink_client_python-1.0.1/src/blueink/subclients/template.py +58 -0
  23. blueink_client_python-1.0.1/src/blueink/subclients/webhook.py +119 -0
  24. blueink_client_python-1.0.1/src/blueink/tests/__init__.py +0 -0
  25. blueink_client_python-1.0.1/src/blueink/tests/test_bundle_helper.py +318 -0
  26. blueink_client_python-1.0.1/src/blueink/tests/test_client.py +531 -0
  27. blueink_client_python-1.0.1/src/blueink/tests/test_person_helper.py +65 -0
  28. blueink_client_python-1.0.1/src/blueink/utils/__init__.py +0 -0
  29. blueink_client_python-1.0.1/src/blueink/utils/testcase.py +33 -0
  30. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink_client_python.egg-info/PKG-INFO +331 -48
  31. blueink_client_python-1.0.1/src/blueink_client_python.egg-info/SOURCES.txt +35 -0
  32. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink_client_python.egg-info/requires.txt +3 -3
  33. blueink-client-python-0.9.3/src/blueink/__init__.py +0 -7
  34. blueink-client-python-0.9.3/src/blueink/bundle_helper.py +0 -264
  35. blueink-client-python-0.9.3/src/blueink/client.py +0 -432
  36. blueink-client-python-0.9.3/src/blueink/model/bundles.py +0 -210
  37. blueink-client-python-0.9.3/src/blueink/test.py +0 -15
  38. blueink-client-python-0.9.3/src/blueink_client_python.egg-info/SOURCES.txt +0 -21
  39. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/LICENSE +0 -0
  40. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/model/__init__.py +0 -0
  41. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink_client_python.egg-info/dependency_links.txt +0 -0
  42. {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink_client_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blueink-client-python
3
- Version: 0.9.3
3
+ Version: 1.0.1
4
4
  Summary: Python Client for Blueink eSignature API
5
5
  Home-page: https://github.com/blueinkhq/blueink-client-python
6
6
  Author: Blueink
@@ -11,24 +11,37 @@ Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
12
  Requires-Python: >=3.8
13
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
14
19
  Provides-Extra: munch
20
+ Requires-Dist: munch>=2.5; extra == "munch"
15
21
  Provides-Extra: requests
22
+ Requires-Dist: requests>=2.31; extra == "requests"
16
23
  Provides-Extra: pydantic
17
- Provides-Extra: email-validator>
18
- License-File: LICENSE
24
+ Requires-Dist: pydantic>=1.9; extra == "pydantic"
25
+ Provides-Extra: email-validator
26
+ Requires-Dist: email-validator>=1.2; extra == "email-validator"
19
27
 
20
28
  # blueink-client-python
29
+ ![Tests](https://github.com/blueinkhq/blueink-client-python/actions/workflows/helper-tests.yml/badge.svg)
30
+ ![Style Checks](https://github.com/blueinkhq/blueink-client-python/actions/workflows/style-checks.yml/badge.svg)
31
+ [![PyPI version](https://badge.fury.io/py/blueink-client-python.svg)](https://pypi.org/project/blueink-client-python/)
32
+ ![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)
21
33
 
22
34
  A Python client library for the BlueInk eSignature API.
23
35
 
24
36
  ## Overview
25
37
 
26
38
  This README provides a narrative overview of using the Blueink Python client, and
27
- includes examples for many common use cases.
39
+ includes examples for many common use cases.
28
40
 
29
41
  Additional resources that might be useful include:
30
42
 
31
- * Examples in the `examples/` directory of this repository
43
+ * Examples at [blueink-client-python-examples](https://github.com/blueinkhq/blueink-client-python-examples)
44
+ repo on GitHub.
32
45
  * The detailed [Blueink API Documentation](https://blueink.com/r/api-docs/), for
33
46
  details on the data returned by each API call.
34
47
 
@@ -49,7 +62,7 @@ pip install blueink-client-python
49
62
 
50
63
  Requests to the Blueink API are made via an instance of the `blueink.Client` class. The
51
64
  `blueink.Client` needs a Blueink private API key. By default the Client looks for
52
- the private key in an environment variable named `BLUEINK_PRIVATE_API_KEY`.
65
+ the private key in an environment variable named `BLUEINK_PRIVATE_API_KEY`.
53
66
 
54
67
  ```bash
55
68
  # In your shell, or in .bashrc or similar
@@ -74,6 +87,21 @@ from blueink import Client
74
87
  client = Client("YOUR_PRIVATE_API_KEY")
75
88
  ```
76
89
 
90
+ The Client also has two modes of operations. By default the Client will raise HTTPError
91
+ exceptions whenever there's an issue between the client and server (eg. HTTP4xx,
92
+ HTTP5xx errors). These come from the `requests` module. If within your application
93
+ you already handle exceptions coming out of `requests` then you should be good.
94
+ If you set `raise_exceptions = False` then these will be returned as
95
+ `NormalizedResponse` objects which are also used for successful communications. See
96
+ below for information about these objects.
97
+
98
+ ```python
99
+ # In your python code, create an instance of the blueink Client
100
+ from blueink import Client
101
+
102
+ client = Client(raise_exceptions=False)
103
+ ```
104
+
77
105
  ### Making API Calls
78
106
 
79
107
  Making API calls with a client instance is easy. For example, to retrieve a list of
@@ -87,26 +115,27 @@ for bundle in bundles:
87
115
  print(bundle.id)
88
116
  ```
89
117
 
90
- The Client class follows a RESTful pattern for making API calls, like so:
118
+ The Client class follows a RESTful pattern for making API calls, like so:
91
119
  `client.[resource].[method]`.
92
120
 
93
121
  The supported "resources" are:
94
122
  * `bundles`
95
123
  * `persons`
96
- * `packets`
124
+ * `packets`
97
125
  * `templates`
126
+ * `webhooks`
98
127
 
99
- The methods correspond to common REST operations:
100
- * `list`
101
- * `retrieve`
102
- * `create`
103
- * `update`
104
- * `delete`.
128
+ The methods correspond to common REST operations:
129
+ * `list()`
130
+ * `retrieve()`
131
+ * `create()`
132
+ * `update()`
133
+ * `delete()`
105
134
 
106
135
  However, note that:
107
- * Not all resources support all methods.
108
- * Some resources support one-off methods that are unique to that resource.
109
- For example the `bundles` resource allows you to retrieve a list of events on
136
+ * Not all resources support all methods.
137
+ * Some resources support one-off methods that are unique to that resource.
138
+ For example the `bundles` resource allows you to retrieve a list of events on
110
139
  the Bundle by calling `client.bundles.list_events()`.
111
140
 
112
141
  Detailed documentation for each resource is below.
@@ -118,18 +147,18 @@ the following attributes.
118
147
 
119
148
  * **response.data**
120
149
 
121
- The json data returned via the API call is accessible via the data attribute. The data
122
- attribute supports dictionary access and dot-notation access (for convenience and less
123
- typing)
150
+ The json data returned via the API call is accessible via the `data` attribute. The
151
+ `data` attribute supports dictionary access and dot-notation access (for convenience
152
+ and less typing).
124
153
 
125
154
  ```python
126
155
  response = client.bundles.retrieve("some bundle ID")
127
-
156
+
128
157
  bundle_data = response.data
129
158
  print(bundle_data['id']) # dictionary-style access
130
159
  print(bundle_data.id) # dot notation access
131
160
  ```
132
-
161
+
133
162
  * **response.request**
134
163
 
135
164
  The request that led to this response. Under-the-hood, the Blueink client library
@@ -138,19 +167,19 @@ the following attributes.
138
167
 
139
168
  * **response.original_response**
140
169
 
141
- Similarly, if you need access to the original response as returned by
142
- Python Requests library, it's accessible as `original_response`.
170
+ Similarly, if you need access to the original response as returned by
171
+ the Python Requests library, it's accessible as `original_response`.
143
172
 
144
173
  * **response.pagination**
145
174
 
146
- Most API calls that return a list of data returned paginated results. If so,
147
- information about the pagination is included in the `pagination` attribute.
175
+ Most API calls that return a list of data returned paginated results. If so,
176
+ information about the pagination is included in the `pagination` attribute.
148
177
 
149
178
  Pagination Example:
150
179
 
151
180
  ```python
152
181
  response = client.persons.list()
153
-
182
+
154
183
  pagination = response.pagination
155
184
  print(pagination.page_number, ' - current page number')
156
185
  print(pagination.total_pages, ' - total pages')
@@ -158,24 +187,63 @@ the following attributes.
158
187
  print(pagination.total_results, ' - total results')
159
188
  ```
160
189
 
161
- See [Lists and Pagination](lists-and-pagination) below.
190
+ See "Requests that Return Lists > Pagination" below.
191
+
192
+ ### Requests that Return Lists
193
+
194
+ #### Filtering and Searching
195
+
196
+ Some Blueink [API endpoints](https://blueink.com/r/api-docs/) support searching and / or
197
+ filtering. In those cases, you can pass querystring parameters to the `list(...)` or
198
+ `paged_list(...)` method on those resources.
199
+
200
+ For example:
162
201
 
163
- ### Lists and Pagination
202
+ ```python
203
+ from blueink import Client, constants
204
+
205
+ client = Client()
206
+
207
+ # Retrieve Bundles with a status of "Complete"
208
+ response = client.bundles.list(status=constants.BUNDLE_STATUS.COMPLETE)
209
+ complete_bundles = response.data
210
+
211
+ # Retrieve Bundles with a status or "Complete" or "Started"
212
+ statuses = ",".join([
213
+ constants.BUNDLE_STATUS.COMPLETE,
214
+ constants.BUNDLE_STATUS.STARTED
215
+ ])
216
+ response = client.bundles.list(status__in=statuses)
217
+ complete_or_started_bundles = response.data
218
+
219
+ # Retrieve Bundles matching a search of "example.com" (which will match signer email
220
+ # addresses). We can pass pagination parameters too.
221
+ response = client.bundles.list(per_page=10, page=2, search="example.com")
222
+ matching_bundles = response.data
223
+
224
+ # Filtering / searching works with pagination iterators / paged_list() calls as well
225
+ iterator = client.bundles.paged_list(search="example.com")
226
+ for paged_response in iterator:
227
+ for bundle in paged_response.data:
228
+ print(bundle.id)
229
+ ```
230
+
231
+ #### Pagination
164
232
 
165
233
  Blueink API calls that return a list of results are paginated - ie, if there
166
- are a lot of results, you need to make multiple requests to retrieve all of those
167
- results, including a page_number parameter (and optionally a page_size parameter)
234
+ are a lot of results, you need to make multiple requests to retrieve all of those
235
+ results, including a `page_number` parameter (and optionally a `page_size` parameter)
168
236
  in each request.
169
237
 
170
- The details of Blueink pagination scheme can be found in the
171
- [API documentation](/r/api-docs/pagination/):
238
+ The details of Blueink pagination scheme can be found in the
239
+ [API documentation](https://blueink.com/r/api-docs/pagination/):
172
240
 
173
241
  This client library provides convenience methods to make looping over
174
242
  paginated results easier. Whenever there is a `list()` method available for a resource,
175
243
  there is a corresponding `paged_list()` method available that returns a
176
- `PaginatedIterator` helper class to make processing the results easier.
244
+ `PaginatedIterator` helper class to make processing the results easier.
177
245
 
178
- You can mostly ignore the details of how the `PaginatedIterator` works. Instead, here
246
+ You can mostly ignore the details of how the `PaginatedIterator` works. Instead, here
179
247
  is an example of looping over a paginated set of Bundles:
180
248
 
181
249
  ```python
@@ -189,15 +257,71 @@ for paged_response in iterator:
189
257
  print(bundle.id)
190
258
  ```
191
259
 
260
+ ## Client Method Index
261
+ Parameters can be found using autocomplete within your IDE. Creates/Updates take a
262
+ Python dictionary as the data field, unless special named methods like
263
+ ```create_from_bundle_helper``` indicate otherwise. List methods can take query params
264
+ as kwargs.
265
+
266
+ ### Bundle Related
267
+ * Create via ```client.bundles.create(...)``` or ```client.bundles.create_from_bundle_helper(...)```
268
+ * List via ```client.bundles.list(...)``` or ```client.bundles.paged_list(...)```
269
+ * Retrieve via ```client.bundles.retrieve(...)```
270
+ * Cancel via ```client.bundles.cancel(...)```
271
+ * List Events via ```client.bundles.list_events(...)```
272
+ * List Files via ```client.bundles.list_files(...)```
273
+ * List Data via ```client.bundles.list_data(...)```
274
+
275
+ ### Person Related
276
+ * Create via ```client.persons.create(...)``` or ```client.persons.create_from_person_helper(...)```
277
+ * List via ```client.persons.list(...)``` or ```client.persons.paged_list(...)```
278
+ * Retrieve via ```client.persons.retrieve(...)```
279
+ * Delete via ```client.persons.delete(...)```
280
+ * Update via ```client.persons.update(...)```
281
+
282
+ ### Packet Related
283
+ * Update via ```client.packets.update(...)```
284
+ * Create Embedded Signing URL via ```client.packets.embed_url(...)```
285
+ * Retrieve COE via ```client.packets.retrieve_coe(...)```
286
+ * Remind via ```client.packets.remind(...)```
287
+
288
+ ### Template Related
289
+ * List via ```client.templates.list(...)``` or ```client.templates.paged_list(...)```
290
+ * Retrieve via ```client.templates.retrieve(...)```
291
+
292
+ ### Webhook Related
293
+
294
+ #### Webhook Client Methods
295
+ * Create via ```client.webhooks.create(...)```
296
+ * List via ```client.webhooks.list(...)```
297
+ * Retrieve via ```client.webhooks.retrieve(...)```
298
+ * Delete via ```client.webhooks.delete(...)```
299
+ * Update via ```client.webhooks.update(...)```
300
+
301
+ #### WebhookExtraHeader Client Methods
302
+ * Create via ```client.webhooks.create_header(...)```
303
+ * List via ```client.webhooks.list_headers(...)```
304
+ * Retrieve via ```client.webhooks.retrieve_header(...)```
305
+ * Delete via ```client.webhooks.delete_header(...)```
306
+ * Update via ```client.webhooks.update_header(...)```
307
+
308
+ #### WebhookEvent Client Methods
309
+ * List via ```client.webhooks.list_events(...)```
310
+ * Retrieve via ```client.webhooks.retrieve_event(...)```
311
+
312
+ #### WebhookDelivery Client Methods
313
+ * List via ```client.webhooks.list_deliveries(...)```
314
+ * Retrieve via ```client.webhooks.retrieve_delivery(...)```
315
+
192
316
  ## Detailed Guide and Examples
193
317
 
194
318
  ### Bundles
195
319
 
196
320
  #### Creating Bundles with the BundleHelper
197
321
 
198
- When creating a Bundle via the API, you need to pass a lot of data in the
199
- `bundle.create(...)` request. This library provides a `BundleHelper` class to ease the
200
- construction of that data.
322
+ When creating a Bundle via the API, you need to pass quite a bit of data in the
323
+ `client.bundle.create(...)` request. To ease the construction of that data, this
324
+ library provides a `BundleHelper` class.
201
325
 
202
326
  Below is an example of using `BundleHelper` to create a Bundle with 1 document,
203
327
  and 2 signers. In this example, the uploaded document is specified via a URL.
@@ -215,7 +339,7 @@ bh = BundleHelper(label="Test Bundle 01",
215
339
  bh.add_cc("bart.simpson@example.com")
216
340
 
217
341
  # Add a document to the Bundle by providing a publicly accessible URL where
218
- # Blueink can download the document to include in the Bundle
342
+ # the Blueink platform can download the document to include in the Bundle
219
343
  doc_key = bh.add_document_by_url("https://www.irs.gov/pub/irs-pdf/fw9.pdf")
220
344
 
221
345
  signer1_key = bh.add_signer(
@@ -256,14 +380,76 @@ Using the `BundleHelper`, you can add files to a Bundle in multiple ways:
256
380
  ```python
257
381
  bh = BundleHelper(...)
258
382
 
259
- # Add a document using a path to the file in the local filesystem
260
- doc1_key = bh.add_document_by_path("/path/to/file/fw4.pdf", "application/pdf")
383
+ # 0) Add a document using a URL to a web resource:
384
+ doc0_key = bh.add_document_by_url("https://www.example.com/example.pdf")
261
385
 
262
- # Add a document that you have already read into a Python bytearray object
263
- pdf_bytearray = read_a_file_into_a_bytearray()
264
- doc2_key = bh.add_document_by_bytearray(pdf_bytearray, "fw4.pdf", "application/pdf")
386
+ # 1) Add a document using a path to the file in the local filesystem
387
+ doc1_key = bh.add_document_by_path("/path/to/file/example.pdf")
388
+
389
+ # 2) Add a document using a UTF-8 encoded Base64 string:
390
+ filename, pdf_b64 = read_a_file_into_b64_string()
391
+ doc02_key = bh.add_document_by_b64(filename, pdf_b64)
392
+
393
+ # 3) Add a document that you have already read into a Python bytearray object
394
+ filename, pdf_bytearray = read_a_file_into_bytearray()
395
+ doc03_key = bh.add_document_by_bytearray(filename, pdf_bytearray)
396
+
397
+ # 4) Add a document as a File object. Make sure to use 'with' or suitably close the file
398
+ # after creating the document.
399
+ with open("/path/to/file/example.pdf", 'rb') as file:
400
+ doc04_key = bh.add_document_by_file(file)
265
401
  ```
266
402
 
403
+ #### Auto-Placement Fields
404
+
405
+ Auto-placement fields allow you to automatically search for text in documents and place
406
+ signature/input fields at those locations with optional offsets. This eliminates the need
407
+ to manually specify exact coordinates for fields.
408
+
409
+ ```python
410
+ from blueink import BundleHelper, Client
411
+
412
+ bh = BundleHelper(
413
+ label="Auto-Placement Example",
414
+ email_subject="Please sign",
415
+ is_test=True,
416
+ )
417
+
418
+ signer_key = bh.add_signer(name="John Doe", email="john@example.com")
419
+ doc_key = bh.add_document_by_url("https://www.irs.gov/pub/irs-pdf/fw9.pdf")
420
+
421
+ # Add auto-placement field that searches for "Signature" text
422
+ bh.add_auto_placement(
423
+ document_key=doc_key,
424
+ kind="sig", # Field type: signature
425
+ search="Signature", # Text to search for
426
+ w=20, # Width
427
+ h=5, # Height
428
+ offset_x=-5, # Move 5 units left from found text
429
+ offset_y=2, # Move 2 units down from found text
430
+ editors=[signer_key],
431
+ )
432
+
433
+ # Add auto-placement for an input field
434
+ bh.add_auto_placement(
435
+ document_key=doc_key,
436
+ kind="inp", # Field type: input
437
+ search="Address", # Text to search for
438
+ w=20,
439
+ h=2,
440
+ offset_x=8, # Move 8 units right from found text
441
+ editors=[signer_key],
442
+ )
443
+
444
+ client = Client()
445
+ response = client.bundles.create_from_bundle_helper(bh)
446
+ ```
447
+
448
+ **Key benefits of auto-placement:**
449
+ - No need to manually find exact coordinates
450
+ - Works with template documents that have consistent text labels
451
+ - Automatically adjusts to text position in the document
452
+ - Can be combined with regular manually-positioned fields
267
453
 
268
454
  #### Retrieval
269
455
 
@@ -306,7 +492,6 @@ Creating a person is similar to a creating a Bundle. There is a PersonHelper to
306
492
  create a person
307
493
 
308
494
  ```python
309
- import json
310
495
  from copy import deepcopy
311
496
  from requests.exceptions import HTTPError
312
497
  from pprint import pprint
@@ -429,7 +614,7 @@ except Exception as e:
429
614
  # Perform a partial update to change the name again
430
615
  third_name = {"name": "Third Name"}
431
616
  try:
432
- result = client.persons.partial_update(result.data.id, third_name)
617
+ result = client.persons.update(result.data.id, third_name, True)
433
618
  pprint(f"Result Partial Update: {result.status}: {result.data}")
434
619
  except HTTPError as e:
435
620
  print(e)
@@ -469,8 +654,6 @@ except HTTPError as e:
469
654
  except Exception as e:
470
655
  print("Error:")
471
656
  print(e)
472
-
473
-
474
657
  ```
475
658
 
476
659
  ### Packets
@@ -507,3 +690,103 @@ template_response = client.templates.retrieve(template_id)
507
690
 
508
691
 
509
692
  ```
693
+ ### Webhooks
694
+
695
+ Webhooks can be interacted with via several methods. Webhooks also have related objects, such as
696
+ ```WebhookExtraHeaders```, ```WebhookEvents```, and ```WebhookDeliveries``` which have their own
697
+ methods to interact with.
698
+
699
+ ```python
700
+ from copy import deepcopy
701
+
702
+ from requests import HTTPError
703
+ from src.blueink import Client
704
+ from src.blueink.constants import EVENT_TYPE
705
+
706
+ WEBHOOK_01 = {
707
+ "url": "https://www.example.com/01/",
708
+ "enabled": True,
709
+ "json": True,
710
+ "event_types": [
711
+ EVENT_TYPE.EVENT_BUNDLE_LAUNCHED,
712
+ EVENT_TYPE.EVENT_BUNDLE_COMPLETE,
713
+ ]
714
+ }
715
+
716
+ WEBHOOK_01_UPDATE = {
717
+ "enabled": False,
718
+ "event_types": [
719
+ EVENT_TYPE.EVENT_PACKET_VIEWED
720
+ ]
721
+ }
722
+
723
+ WEBHOOK_01_EXTRA_HEADER_A = {
724
+ "name": "Courage_The_Cowardly_Webhook",
725
+ "value": "Muriel Bagge",
726
+ "order": 0,
727
+ }
728
+
729
+ client = Client()
730
+
731
+ # --------------
732
+ # Attempt posting a new Webhook
733
+ # --------------
734
+ try:
735
+ create_resp = client.webhooks.create(data=WEBHOOK_01)
736
+ webhook = create_resp.data
737
+ print(f"Created webhook {webhook.id}")
738
+ except HTTPError as e:
739
+ print("Error Creating Webhook: ")
740
+ print(e)
741
+
742
+ # --------------
743
+ # Update Webhook
744
+ # --------------
745
+ try:
746
+ update_resp = client.webhooks.update(webhook.id, WEBHOOK_01_UPDATE)
747
+ webhook = update_resp.data
748
+ print(f"Updated webhook {webhook.id}")
749
+ except HTTPError as e:
750
+ print("Error Updating Webhook: ")
751
+ print(e)
752
+
753
+ # --------------
754
+ # Create and add an ExtraHeader to the above Webhook
755
+ # --------------
756
+ extra_header_data = deepcopy(WEBHOOK_01_EXTRA_HEADER_A)
757
+ extra_header_data["webhook"] = webhook.id
758
+ try:
759
+ header_create_resp = client.webhooks.create_header(data=extra_header_data)
760
+ header = header_create_resp.data
761
+ print(f"Added ExtraHeader {header} to {webhook.id}")
762
+ except HTTPError as e:
763
+ print("Error Creating ExtraHeader: ")
764
+ print(e)
765
+
766
+
767
+ # --------------
768
+ # List Webhooks
769
+ # --------------
770
+ try:
771
+ list_resp = client.webhooks.list()
772
+ webhook_list = list_resp.data
773
+
774
+ print(f"Found {len(webhook_list)} Webhooks")
775
+ for wh in webhook_list:
776
+ print(f" - Webhook ID: {wh.id} to {wh.url}")
777
+
778
+
779
+ except HTTPError as e:
780
+ print("Error Listing Webhooks: ")
781
+ print(e)
782
+
783
+ # --------------
784
+ # Delete webhook
785
+ # --------------
786
+ try:
787
+ delete_resp = client.webhooks.delete(webhook.id)
788
+ print(f"Deleted Webhook {webhook.id}")
789
+ except HTTPError as e:
790
+ print(f"Error Deleting Webhooks {webhook.id}")
791
+ print(e)
792
+ ```