From c5354ee5e6c20ea4beb8b345406875eb6936d1f4 Mon Sep 17 00:00:00 2001 From: rh0mb Date: Tue, 22 Jul 2025 10:58:44 -0600 Subject: [PATCH 1/4] Fix: serialize allowed_signature_methods correctly as JSON array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixes an issue where the SDK was incorrectly sending `allowed_signature_methods` as a Python string instead of a JSON array, causing a 500 Internal Server Error when using mixed signature types like `FEA` and `FESCV`. ### What’s changed - In the `Document.create()` method, the `allowed_signature_methods` field is now properly serialized using `json.dumps()` before being sent in the form data. ### Why The Mifiel API expects `allowed_signature_methods` to be a JSON array (e.g. `["FEA"]`), not a Python string like `"['FEA']"` or `"FEA,FESCV"`. The previous version of the SDK unintentionally broke this compatibility when posting multipart/form-data requests. --- mifiel/document.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mifiel/document.py b/mifiel/document.py index 1df60cd..1dfab23 100644 --- a/mifiel/document.py +++ b/mifiel/document.py @@ -50,11 +50,10 @@ def create(client, signatories, **kwargs): if 'viewers' in data: viewers = data.pop('viewers') - for index, item in enumerate(viewers): - for key, val in item.items(): - data.update( - {'viewers[' + str(index) + '][' + str(key) + ']': val} - ) + for index, item in enumerate(signatories): + for key, val in item.items(): + value = json.dumps(val) if key == 'allowed_signature_methods' and isinstance(val, list) else val + data[f'signatories[{index}][{key}]'] = value if 'callback_url' in kwargs: data['callback_url'] = kwargs.get('callback_url') if file: From 9ef4552fe399df06b363c6898d794393119285fe Mon Sep 17 00:00:00 2001 From: rh0mb Date: Tue, 22 Jul 2025 13:30:44 -0600 Subject: [PATCH 2/4] Fix: send allowed_signature_methods as array using [] syntax in multipart/form-data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, allowed_signature_methods was being serialized either as a plain string or using indexed keys (e.g., [0], [1]), which resulted in incorrect array parsing by the Mifiel API backend. This commit updates the serialization to use the `[]` syntax: signatories[0][allowed_signature_methods][]=FEA signatories[0][allowed_signature_methods][]=FESCV This is a widely supported format for arrays in multipart/form-data and ensures compatibility with the backend’s expected structure. Also removes the use of json.dumps, which was turning arrays into escaped strings instead of sending them as proper arrays. --- mifiel/document.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mifiel/document.py b/mifiel/document.py index 1dfab23..76a1201 100644 --- a/mifiel/document.py +++ b/mifiel/document.py @@ -44,16 +44,19 @@ def create(client, signatories, **kwargs): data = kwargs.copy() for index, item in enumerate(signatories): for key, val in item.items(): - data.update( - {'signatories[' + str(index) + '][' + str(key) + ']': val} - ) + if key == 'allowed_signature_methods' and isinstance(val, list): + for method in val: + data[f'signatories[{index}][{key}][]'] = method + else: + data[f'signatories[{index}][{key}]'] = val if 'viewers' in data: viewers = data.pop('viewers') - for index, item in enumerate(signatories): - for key, val in item.items(): - value = json.dumps(val) if key == 'allowed_signature_methods' and isinstance(val, list) else val - data[f'signatories[{index}][{key}]'] = value + for index, item in enumerate(viewers): + for key, val in item.items(): + data.update( + {'viewers[' + str(index) + '][' + str(key) + ']': val} + ) if 'callback_url' in kwargs: data['callback_url'] = kwargs.get('callback_url') if file: From ede92d87375db22cea6abb1613365dc24020b716 Mon Sep 17 00:00:00 2001 From: rh0mb Date: Tue, 22 Jul 2025 14:50:22 -0600 Subject: [PATCH 3/4] Fix: properly support multiple allowed_signature_methods per signer Replaced internal data structure with a list of tuples to correctly handle multiple allowed_signature_methods for each signer in multipart/form-data. This change ensures repeated keys are preserved, allowing the backend to receive arrays like: signatories[0][allowed_signature_methods][]=FEA signatories[0][allowed_signature_methods][]=FESCV instead of silently overwriting values when using a dict. --- mifiel/document.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mifiel/document.py b/mifiel/document.py index 76a1201..f74e4e1 100644 --- a/mifiel/document.py +++ b/mifiel/document.py @@ -46,9 +46,13 @@ def create(client, signatories, **kwargs): for key, val in item.items(): if key == 'allowed_signature_methods' and isinstance(val, list): for method in val: - data[f'signatories[{index}][{key}][]'] = method + data.append( + (f'signatories[{index}][{key}][]', method) + ) else: - data[f'signatories[{index}][{key}]'] = val + data.append( + (f'signatories[{index}][{key}]', val) + ) if 'viewers' in data: viewers = data.pop('viewers') From ae69b35af33c444261536942ec68c9d5014959ab Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Apr 2026 16:40:26 +0000 Subject: [PATCH 4/4] fix(document): build multipart data dict before array fields Document.create mistakenly called dict.append when serializing signatories. Store scalar signatory fields on the dict, collect allowed_signature_methods as repeated bracket keys, then convert to a list of tuples for requests when needed. Apply callback_url and original_hash (dhash) before conversion so dict operations stay valid. Co-authored-by: Genaro Madrid --- mifiel/document.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mifiel/document.py b/mifiel/document.py index f74e4e1..35f7366 100644 --- a/mifiel/document.py +++ b/mifiel/document.py @@ -42,17 +42,16 @@ def create(client, signatories, **kwargs): raise ValueError('A name is required when using hash') data = kwargs.copy() + signatory_array_fields = [] for index, item in enumerate(signatories): for key, val in item.items(): if key == 'allowed_signature_methods' and isinstance(val, list): for method in val: - data.append( + signatory_array_fields.append( (f'signatories[{index}][{key}][]', method) ) else: - data.append( - (f'signatories[{index}][{key}]', val) - ) + data[f'signatories[{index}][{key}]'] = val if 'viewers' in data: viewers = data.pop('viewers') @@ -62,13 +61,18 @@ def create(client, signatories, **kwargs): {'viewers[' + str(index) + '][' + str(key) + ']': val} ) - if 'callback_url' in kwargs: data['callback_url'] = kwargs.get('callback_url') + if 'callback_url' in kwargs: + data['callback_url'] = kwargs.get('callback_url') + if dhash: + data['original_hash'] = data.pop('dhash') + + if signatory_array_fields: + data = list(data.items()) + signatory_array_fields + if file: mimetype = mimetypes.guess_type(file)[0] _file = open(file, 'rb') file = {'file': (basename(_file.name), _file, mimetype)} - if dhash: - data['original_hash'] = data.pop('dhash') doc = Document(client) doc.process_request('post', data=data, files=file)