Inconsistent Prefill Tags Behavior from Sign Request API

Hi,

I am currently using the Sign Request API to prefill a pre-created sign template with some data for a signer. Most of the time, my fields prefill completely as expected. (I’m passing them in prefill_tags => {document_tag_id: "tag_name", "text_value": "value" } format, and that seems to be working.)

However, every once in a while (maybe like one in every 4 sign requests?) my fields don’t populate correctly and are blank for the signer. I haven’t been able to identify any consistent similarities between these cases, and the success/failure cases have been with the exact same code and request pattern. Any thoughts on what might be going on here?

Hi @user147 , welcome to the forum.

It looks like the sign request goes through. I was thinking on the possibility of some data conversion error, (are all your tags of type text?) but I would expect some exception and that would prevent the sign request doesn’t go through.

Could it be a conversion error on your app side that silently sets the tags to empty?

Can you help us with an attempt to reproduce this?

Cheers

Hi @rbarbosa , appreciate the response. I’ll do my best to help reproduce! I won’t be able to offer specific code snippets, as it’s part of a closed source product.

What I can say after just trying to reproduce is that even in the case where the tag isn’t prefilled in the Sign UI, the 201 response I get back from the API’s body includes the prefill_tags info with the string I expect, i.e. this is included in the response is this:

"prefill_tags": [
       {
         "checkbox_value": null,
         "date_value": null,
         "document_tag_id": "my_tag_id",
         "text_value": "value_passed"
       }
     ]

So my strings seem to be making it over from my app successfully. I’m not sure if the date_value and checkbox_value fields should be included, but they’re empty so that follows what I’d expect to receive back too.

That is fine, I’ll work with what you can give us.

This is interesting, it seems the value of the tags actually end up on the sign request.

Would it be possible that the sign request has more than one signer and there are tags with duplicate id’s?

What I mean is could there be a miss match or duplication in the tag id’s versus the signer id?

For example, consider this tag: [[t|1|id:tag_full_name|n:enter your complete name]], where t stands for text and 1 is for the first signer. If I have a [[t|2|id:tag_full_name|n:enter your complete name]] then the tag id tag_full_name could cause confusion, even if pointing to signer 2.

For clarification, are you using a document (word/pdf) with tags as the example above or are you using a sign template created inside the box app?

A tagged document looks like this:

A template created in the box app looks like this:

Let us know.

Cheers

I’m using a template created in the Box app. I do have multiple signers, however they have different ids and the order does match what I’m sending in the request. I also only have one field that has the tag ID I’m targeting, which is designated for my first signer.

Hi @rbarbosa , just following up to see if there’s been any movement or more info uncovered here, as this would really enhance a feature we’re currently working on and hoping to ship soon. Thanks for the help!

Hi @user147 ,

This is not forgotten.

I’m setting up a test with a template and then generate 10 or so requests and see if I get the same symptoms.

On your side are some of the request fields still coming out empty?

I’ll keep you posted.

Awesome, really appreciate the update. Behavior is still the same on my end, fields are sometimes filled and sometimes not with the same input and output as far as the API is concerned.

Thanks again.

Hi @user147

I wasn’t able to replicate this…

I’ve created 10 signature request with the following result:

Simple sign request: f025d707-42a9-4b0e-bc3a-bed548e979a6
  Status: sent
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+0@gmail.com

Simple sign request: 7917303c-b6b7-463d-8fcd-b11c91e31d52
  Status: sent
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+1@gmail.com

Simple sign request: 31b868b1-5646-44fd-826e-60712db78535
  Status: created
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+2@gmail.com

Simple sign request: 3f9e5131-67a2-4c43-a93b-51677ec8a909
  Status: created
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+3@gmail.com

Simple sign request: 0d60e646-db23-4fc2-83a0-565302e3eb6d
  Status: sent
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+4@gmail.com

Simple sign request: b09b01b5-a7fe-455f-82ed-19aa456db908
  Status: sent
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+5@gmail.com

Simple sign request: fb4a05e7-bf68-4684-ad9e-9697fe76da5c
  Status: sent
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+6@gmail.com

Simple sign request: f3556afe-829a-40bf-a2a2-287d8bdeb1fe
  Status: sent
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+7@gmail.com

Simple sign request: fe49835f-0ab6-40a2-88ff-60d1ff43b17e
  Status: created
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+8@gmail.com

Simple sign request: c2242ce6-b3d5-40ee-9069-01d2d15aa22f
  Status: created
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+9@gmail.com

Notice, some of the status are sent, and some were created, this is an asynchronous process so it may take some time to actually send all the requests.

After getting the email, and signing each one of them, I listed the details from the user requests using this script:

def check_sign_request_by_id(client: Client, sign_request_id: str):
    """Check sign request by id"""
    sign_request = client.sign_requests.get_sign_request_by_id(sign_request_id)

    print(f"\nSimple sign request: {sign_request.id}")
    print(f"  Status: {sign_request.status.value}")

    print(f"  Signers: {len(sign_request.signers)}")
    for signer in sign_request.signers:
        print(f"    {signer.role.value}: {signer.email}")
        for input in signer.inputs:
            content_type = input.content_type
            value = None

            if content_type == SignRequestSignerInputContentTypeField.CHECKBOX:
                value = input.checkbox_value
            elif content_type == SignRequestSignerInputContentTypeField.TEXT:
                value = input.text_value
            elif (
                content_type
                == SignRequestSignerInputContentTypeField.FULL_NAME
            ):
                value = input.text_value
            elif content_type == SignRequestSignerInputContentTypeField.DATE:
                value = input.date_value

            print(
                f"      {input.type.value}: {value if value is not None else '<other>'}"
            )

and in the main script:

    # list sign requests
    user_sign_requests = client.sign_requests.get_sign_requests()
    print(f"\nSign requests: {len(user_sign_requests.entries)}")
    for sign_request in user_sign_requests.entries:
        check_sign_request_by_id(client, sign_request.id)

Resulting in:

Sign requests: 10

Simple sign request: c2242ce6-b3d5-40ee-9069-01d2d15aa22f
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+9@gmail.com
      date: 2023-12-08
      text: jjjj
      signature: <other>

Simple sign request: fe49835f-0ab6-40a2-88ff-60d1ff43b17e
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+8@gmail.com
      date: 2023-12-08
      text: iii
      signature: <other>

Simple sign request: f3556afe-829a-40bf-a2a2-287d8bdeb1fe
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+7@gmail.com
      date: 2023-12-08
      text: hhh
      signature: <other>

Simple sign request: fb4a05e7-bf68-4684-ad9e-9697fe76da5c
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+6@gmail.com
      text: ggg
      signature: <other>
      date: 2023-12-08

Simple sign request: b09b01b5-a7fe-455f-82ed-19aa456db908
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+5@gmail.com
      signature: <other>
      text: fff
      date: 2023-12-08

Simple sign request: 0d60e646-db23-4fc2-83a0-565302e3eb6d
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+4@gmail.com
      date: 2023-12-08
      text: eee
      signature: <other>

Simple sign request: 3f9e5131-67a2-4c43-a93b-51677ec8a909
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+3@gmail.com
      date: 2023-12-08
      text: ddd
      signature: <other>

Simple sign request: 31b868b1-5646-44fd-826e-60712db78535
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+2@gmail.com
      text: ccc
      date: 2023-12-08
      signature: <other>

Simple sign request: 7917303c-b6b7-463d-8fcd-b11c91e31d52
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+1@gmail.com
      signature: <other>
      text: bbb
      date: 2023-12-08

Simple sign request: f025d707-42a9-4b0e-bc3a-bed548e979a6
  Status: signed
  Signers: 2
    final_copy_reader: ...@gmail.com
    signer: ...+0@gmail.com
      date: 2023-12-08
      signature: <other>
      text: aaa

Notice that all status are sent, meaning the sign request is complete and without errors, and I can access the input data.

So what could possibly be wrong?

Each input has a type and stores the value in different properties. For example an input type of full_name stores the data under input.text_value, however an input type of date, stored the data under input.date_value.
Could there be a miss match between the input types and where your app is getting the values from?

The other option, but I haven’t been able to replicate, is that the sign request is not finished yet or has an error. Have your app check for the signed state before getting the values.

I’m running out of options here, so if this doesn’t yield any results on your side, my recommendation is to open a support ticket.

Finally I’ve created this sample script using the Gen Python SDK, and a modified sign workshop script (see below).

import logging

from utils.box_client_oauth import ConfigOAuth, get_client_oauth
from box_sdk_gen.client import BoxClient as Client
from box_sdk_gen.schemas import (
    SignRequest,
    SignRequestCreateSigner,
    SignRequestSignerInputContentTypeField,
    FolderBaseTypeField,
    FolderMini,
)

# from utils.oauth_callback import open_browser

logging.basicConfig(level=logging.INFO)
logging.getLogger("box_sdk_gen").setLevel(logging.CRITICAL)

SIGN_DOCS_FOLDER = "234102987614"

SIMPLE_PDF = "1355143830404"
SIMPLE_DOC = "1358077513913"
CONTRACT = "1358047520478"

SIGNER_A = "YOUR_EMAIL+A@gmail.com"
SIGNER_A_PHONE = "+15554443322"

SIGNER_B = "YOUR_EMAIL+B@gmail.com"

APPROVER = "YOUR_EMAIL+APPROVER@gmail.com"
FINAL_COPY = "YOUR_EMAIL+FINAL_COPY@gmail.com"

TEMPLATE_SIMPLE = "94e3815b-f7f5-4c2c-8a26-e9ba5c486031"


def create_sign_request(client: Client, template_id: str, signer_email: str):
    """Create sign request from template"""
    parent_folder = FolderMini(
        id=SIGN_DOCS_FOLDER, type=FolderBaseTypeField.FOLDER
    )

    signer = SignRequestCreateSigner(
        email=signer_email,
    )

    sign_request = client.sign_requests.create_sign_request(
        signers=[signer],
        parent_folder=parent_folder,
        template_id=template_id,
    )

    return sign_request


def check_sign_request_by_id(client: Client, sign_request_id: str):
    """Check sign request by id"""
    sign_request = client.sign_requests.get_sign_request_by_id(sign_request_id)

    print(f"\nSimple sign request: {sign_request.id}")
    print(f"  Status: {sign_request.status.value}")

    print(f"  Signers: {len(sign_request.signers)}")
    for signer in sign_request.signers:
        print(f"    {signer.role.value}: {signer.email}")
        for input in signer.inputs:
            content_type = input.content_type
            value = None

            if content_type == SignRequestSignerInputContentTypeField.CHECKBOX:
                value = input.checkbox_value
            elif content_type == SignRequestSignerInputContentTypeField.TEXT:
                value = input.text_value
            elif (
                content_type
                == SignRequestSignerInputContentTypeField.FULL_NAME
            ):
                value = input.text_value
            elif content_type == SignRequestSignerInputContentTypeField.DATE:
                value = input.date_value

            print(
                f"      {input.type.value}: {value if value is not None else '<other>'}"
            )


def main():
    """Simple script to demonstrate how to use the Box SDK"""
    conf = ConfigOAuth()
    client = get_client_oauth(conf)

    user = client.users.get_user_me()
    print(f"\nHello, I'm {user.name} ({user.login}) [{user.id}]")

    # # create 10 sign request from template

    # for i in range(10):
    #     sign_request = create_sign_request(
    #         client, TEMPLATE_SIMPLE, "your_email+" + str(i) + "@gmail.com"
    #     )
    #     check_sign_request_by_id(client, sign_request.id)

    # list sign requests
    user_sign_requests = client.sign_requests.get_sign_requests()
    print(f"\nSign requests: {len(user_sign_requests.entries)}")
    for sign_request in user_sign_requests.entries:
        check_sign_request_by_id(client, sign_request.id)


if __name__ == "__main__":
    main()

Let us know if this helps

Cheers