OneSpan Sign How To: Requesting Attachments
As a sender, you may want to request a signer to upload an attachment during the signing ceremony. For example, as a car insurance broker, you may require your client to provide a copy of his/her driver’s license along with the signed documents. After the requested attachments have been provided, you will have the option of accepting or rejecting the attachment. In the event of a refusal, you will be able to provide feedback in the form of a short message and the signer will have the opportunity to upload a different file. In this blog, I will show you how to request, download, and accept/refuse file attachments with the OneSpan Sign Java SDK, .NET SDK, and REST API.
The Code
Now that we know our objective, let’s dive straight into the code. I will cover the exact same information in each segment. Go ahead and skip to the section which applies to you. Full example code for this blog can be found in the Developer Community Code Share: Java, .NET, and REST.
Java SDK
I’ll start with the Java SDK. Below is a sample code on how you would edit your signer block to request a file attachment. The withDescription()
method allows you to provide a description to the signer about the file upload you’re looking for and the isRequiredAttachment()
method defines that you are requiring the attachment. Neither of these are mandatory when building your AttachmentRequirement
object. If you need a comparison to the basic document object creation or if this is your first time creating a package with the Java SDK, see this blog.
.withSigner(newSignerWithEmail("[email protected]") .withFirstName("John") .withLastName("Doe") .withCustomId("Signer1") .withAttachmentRequirement(newAttachmentRequirementWithName("Driver's license") .withDescription("Please upload a copy of your driver’s license.") .isRequiredAttachment() .build()))
Accordingly, you may also want to query the status of each attachment in your package. OneSpan Sign will return AttachmentRequirement
objects as a list. The code below will loop through each AttachmentRequirement
object and print out the name, status, and id of each requested attachment for that particular signer.
DocumentPackage myPackage = client.getPackage(packageId); List<AttachmentRequirement> signer1Attachments = myPackage.getSigner("[email protected]").getAttachmentRequirements(); for(AttachmentRequirement attachment : signer1Attachments){ System.out.println(attachment.getName() + " " + attachment.getStatus() + " " + attachment.getId()); }
Furthermore, you have the option of downloading attachments in three different manners:
- A single attachment (requires the attachment id)
- All attachments in a package
- All attachments from a particular signer
//Download individual attachment DownloadedFile downloadedAttachment = client.getAttachmentRequirementService().downloadAttachmentFile(packageId, attachmentId); Files.saveTo(downloadedAttachment.getContents(), downloadedAttachment.getFilename()); //Download all attachments in package DownloadedFile downloadedAllAttachmentsForPackage = client.getAttachmentRequirementService().downloadAllAttachmentFilesForPackage(packageId); Files.saveTo(downloadedAllAttachmentsForPackage.getContents(), "downloadedAllAttachmentsForPackage.zip"); //Download all attachments for signer in package DownloadedFile downloadedAllAttachmentsForSigner1InPackage = client.getAttachmentRequirementService().downloadAllAttachmentFilesForSignerInPackage(myPackage, signer1); Files.saveTo(downloadedAllAttachmentsForSigner1InPackage.getContents(), "downloadedAllAttachmentsForSigner.zip");
After reviewing the attachment, you have the ability to refuse or accept the attachment. In the latter situation, you can include feedback in the form of a small message explaining the refusal.
client.getAttachmentRequirementService().rejectAttachment(packageId, signer1, "Driver's license", "Expired driver's license");
It is important to note that packages with required attachments will not auto-complete. This is for the sender to have the opportunity to review the attachment and accept/reject it. If all the required attachments have been uploaded and approved, you can complete the package by updating the status of your package to COMPLETED.
DocumentPackage myPackage = client.getPackage(packageId); myPackage.setStatus(PackageStatus.COMPLETED); client.updatePackage(packageId, myPackage);
.NET SDK
Next, I will cover the .NET SDK. Below is a sample code on how you would edit your signer block to request a file attachment. The WithDescription()
method allows you to provide a description to the signer about the file upload you’re looking for and the IsRequiredAttachment()
method defines that you are requiring the attachment. Neither of these are mandatory when building your AttachmentRequirement
object. If you need a comparison to the basic document object creation or if this is your first time creating a package with the .NET SDK, see this blog.
.WithSigner(SignerBuilder.NewSignerWithEmail("[email protected]") .WithFirstName("John") .WithLastName("Doe") .WithCustomId("Signer1") .WithAttachmentRequirement(AttachmentRequirementBuilder.NewAttachmentRequirementWithName("Driver's license") .WithDescription("Please upload a copy of your driver’s license.") .IsRequiredAttachment() .Build()))
Accordingly, you may also want to query the status of each attachment in your package. OneSpan Sign will return AttachmentRequirement
objects as a list. The code below will loop through each AttachmentRequirement
object and print out the name, status, and id of each requested attachment for that particular signer.
DocumentPackage myPackage = client.GetPackage(packageId); IList<AttachmentRequirement> signer1Attachments = myPackage.GetSigner("[email protected]").Attachments; foreach (AttachmentRequirement attachment in signer1Attachments) { Debug.WriteLine(attachment.Name + " " + attachment.Status + " " + attachment.Id); }
Furthermore, you can download attachments in three different ways:
- A single attachment (requires the attachment id)
- All attachments in a package
- All attachments from a particular signer
//Download individual attachment DownloadedFile downloadedAttachment = client.AttachmentRequirementService.DownloadAttachmentFile(packageId, attachmentId); System.IO.File.WriteAllBytes(downloadedAttachment.Filename, downloadedAttachment.Contents); //Download all attachments in package DownloadedFile downloadedAllAttachmentsForPackage = client.AttachmentRequirementService.DownloadAllAttachmentFilesForPackage(packageId); System.IO.File.WriteAllBytes("downloadedAllAttachmentsForPackage.zip", downloadedAllAttachmentsForPackage.Contents); //Download all attachments for signer in package DownloadedFile downloadedAllAttachmentsForSigner1InPackage = client.AttachmentRequirementService.DownloadAllAttachmentFilesForSignerInPackage(myPackage, signer1); System.IO.File.WriteAllBytes("downloadedAllAttachmentsForSigner.zip", downloadedAllAttachmentsForSigner1InPackage.Contents);
After reviewing the attachment, you have the ability to refuse the attachment. In this situation, you can include feedback in the form of a small message explaining the refusal.
client.AttachmentRequirementService.RejectAttachment(packageId, signer1, "Driver's license", "Expired driver's license");
It is important to note that packages with required attachments will not auto-complete. This is for the sender to have the opportunity to review the attachment and accept/reject it. If all the required attachments have been uploaded and approved, you can complete the package by updating the status of your package to COMPLETED.
DocumentPackage myPackage = eslClient.GetPackage(packageId); myPackage.Status = DocumentPackageStatus.COMPLETED; eslClient.UpdatePackage(packageId, myPackage);
REST API
Finally, I will cover the REST API. The sample JSON below shows how to edit your roles object to request an attachment. If you need a comparison to the basic document object creation or if this is your first time creating a package with the REST API, see this blog.
[code language="javascript"]{ "roles": [ { "id": "client", "type": "SIGNER", "index": 1, "attachmentRequirements": [ { "description": "Please upload a scanned copy of your driver's license.", "required": true, "id": "lD6p5QnWk905", "data": null, "status": "INCOMPLETE", "comment": "", "name": "Driver's license" } ], "signers": [ { "firstName": "John", "lastName": "Smith", "email": "[email protected]" } ], "name": "client" }, { "id": "contractor", "type": "SIGNER", "index": 2, "signers": [ { "firstName": "Bob", "lastName": "Murray", "email": "[email protected]" } ], "name": "contractor" } ] } Accordingly, you may also want to query the status of each attachment in your package. The code below will retrieve each attachmentRequirements object in your package.
HttpClient myClient = new HttpClient(); myClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", apiKey); myClient.DefaultRequestHeaders.Add("Accept", "application/json"); var roles = myClient.GetAsync(new Uri(apiUrl + "/packages/{packageId}/roles")).Result; JObject roles_json = JObject.Parse(roles.Content.ReadAsStringAsync().Result); var results = roles_json["results"]; foreach (var attachment in results) { var attach = attachment["attachmentRequirements"]; Debug.WriteLine(attach); }
To download an individual attachment, you will need the package and attachment id. The sample code below shows you how to make your request:
var attachment = myClient.GetAsync(new Uri(API_URL + "/packages/{packageId}/attachment/{attachmentId}")).Result; ByteArrayContent content = new ByteArrayContent(attachment.Content.ReadAsByteArrayAsync().Result); File.WriteAllBytes("PATH_TO_DOWNLOAD", content.ReadAsByteArrayAsync().Result);
To download all attachments as a zip file in a package, you will make your request to https://sandbox.onespan.com/api/packages/{packageId}/attachment/zip
. To download all attachments as a zip file for a particular signer, you will make your request to https://sandbox.onespan.com/api/packages/{packageId}/attachment/zip/{roleId}
. After reviewing the attachment, you have the ability to accept or refuse the attachment. In the latter situation, you can include feedback in the form of a small message explaining the refusal. To refuse an attachment, you will a PUT request to https://sandbox.onespan.com/api/packages/{packageId}/roles/{roleId}
with the following JSON: [code language="javascript"]{ "attachmentRequirements": [ { "id": "q66CYiDrxTU1", "status": "REJECTED", "comment": "Invalid copy." } ] } It is important to note that packages with required attachments will not auto-complete. This is for the sender to have the opportunity to review the attachment and accept/reject it. If all the required attachments have been uploaded and approved, you can complete the package by making a PUT request to https://sandbox.onespan.com/api/packages/{packageId}
, with the following JSON: [code language="javascript"]{ "status" : "COMPLETED" }
Running Your Code
Once you’ve run your code, if you log into OneSpan Sign and head to the signer settings in your package, you should find your attachment requests in the attachments tab.
During the signing ceremony, the signer will be presented to the dialog below requesting to upload the required attachment.
If you browse to your workspace directory, you will find your downloaded attachments.
There you go. You have successfully requested attachments! If you have questions regarding this blog or anything else concerning integrating OneSpan Sign into your application, visit the developer community forums: https://developer.onespan.com.
That's it from me. Thank you for reading! If you found this post helpful, please share it on Facebook, Twitter, or LinkedIn.
Haris Haidary
Junior Technical Evangelist