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.
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/PKG-INFO +331 -48
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/README.md +320 -45
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/pyproject.toml +6 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/setup.cfg +9 -5
- blueink_client_python-1.0.1/src/blueink/__init__.py +8 -0
- blueink_client_python-1.0.1/src/blueink/bundle_helper.py +580 -0
- blueink_client_python-1.0.1/src/blueink/client.py +76 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/constants.py +10 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/endpoints.py +29 -0
- blueink_client_python-1.0.1/src/blueink/model/bundles.py +312 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/model/persons.py +2 -4
- blueink_client_python-1.0.1/src/blueink/model/webhook.py +30 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/paginator.py +6 -5
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/person_helper.py +20 -9
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/request_helper.py +28 -7
- blueink_client_python-1.0.1/src/blueink/subclients/__init__.py +0 -0
- blueink_client_python-1.0.1/src/blueink/subclients/bundle.py +322 -0
- blueink_client_python-1.0.1/src/blueink/subclients/envelope_template.py +64 -0
- blueink_client_python-1.0.1/src/blueink/subclients/packet.py +57 -0
- blueink_client_python-1.0.1/src/blueink/subclients/person.py +124 -0
- blueink_client_python-1.0.1/src/blueink/subclients/subclient.py +52 -0
- blueink_client_python-1.0.1/src/blueink/subclients/template.py +58 -0
- blueink_client_python-1.0.1/src/blueink/subclients/webhook.py +119 -0
- blueink_client_python-1.0.1/src/blueink/tests/__init__.py +0 -0
- blueink_client_python-1.0.1/src/blueink/tests/test_bundle_helper.py +318 -0
- blueink_client_python-1.0.1/src/blueink/tests/test_client.py +531 -0
- blueink_client_python-1.0.1/src/blueink/tests/test_person_helper.py +65 -0
- blueink_client_python-1.0.1/src/blueink/utils/__init__.py +0 -0
- blueink_client_python-1.0.1/src/blueink/utils/testcase.py +33 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink_client_python.egg-info/PKG-INFO +331 -48
- blueink_client_python-1.0.1/src/blueink_client_python.egg-info/SOURCES.txt +35 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink_client_python.egg-info/requires.txt +3 -3
- blueink-client-python-0.9.3/src/blueink/__init__.py +0 -7
- blueink-client-python-0.9.3/src/blueink/bundle_helper.py +0 -264
- blueink-client-python-0.9.3/src/blueink/client.py +0 -432
- blueink-client-python-0.9.3/src/blueink/model/bundles.py +0 -210
- blueink-client-python-0.9.3/src/blueink/test.py +0 -15
- blueink-client-python-0.9.3/src/blueink_client_python.egg-info/SOURCES.txt +0 -21
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/LICENSE +0 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink/model/__init__.py +0 -0
- {blueink-client-python-0.9.3 → blueink_client_python-1.0.1}/src/blueink_client_python.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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
|
-
|
|
18
|
-
|
|
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
|
+

|
|
30
|
+

|
|
31
|
+
[](https://pypi.org/project/blueink-client-python/)
|
|
32
|
+

|
|
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
|
|
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
|
|
122
|
-
attribute supports dictionary access and dot-notation access (for convenience
|
|
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
|
|
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
|
-
|
|
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
|
|
199
|
-
`bundle.create(...)` request.
|
|
200
|
-
|
|
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
|
|
260
|
-
|
|
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
|
|
263
|
-
|
|
264
|
-
|
|
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.
|
|
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
|
+
```
|