Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Struggeling with Formfileds on an agreement

New Here ,
Nov 01, 2024 Nov 01, 2024

Hi all, I am really struggeling with getting the formfields working from a custom C# app, I have tried adding the formfiels on create of the agreement and nothing show, I have tried the Post and the PUT on "

https://api.na3.adobesign.com:443/api/rest/v6/agreements/{agreementID}/formFields
"Post" returs error "Invalid Agreement ID"

Put Just does not add antying on the agreement, I always only get a signature box on the last page, I want a signature box on each page for mutiple documents, please see my nethod as cureent:

public async Task<string> UploadDocumentsReturnAgreementId(string _OauthUrl, string _ClientId, string _ClientSecret, string _RefreshToken, string _initialApiAccessPoint, string _apiUserEmail, string pstrDelimitedDocumentPaths, string pstrToEmail, long pintProcessInstanceID)
{
try
{
TimeSpan currentTimeOnly = DateTime.Now.TimeOfDay;
string[] larrDocumentPaths = pstrDelimitedDocumentPaths.Split('|');
List<string> pstrDocumentPaths = new List<string>(larrDocumentPaths);

_bearerToken = RefreshToken(_OauthUrl, _ClientId, _ClientSecret, _RefreshToken);
_apiAccessPoint = await GetApiAccessPoint(_initialApiAccessPoint, _bearerToken);
_apiAccessPoint = _apiAccessPoint.Replace(".com/", ".com");

var transientDocumentIds = new List<string>();
var pageCounts = new List<int>();

// Upload documents and get transient document IDs
foreach (var documentPath in pstrDocumentPaths)
{
if (!String.IsNullOrEmpty(documentPath))
{
var pageCount = GetPageCount(documentPath);
pageCounts.Add(pageCount);

var request = (HttpWebRequest)WebRequest.Create($"{_apiAccessPoint}/api/rest/v6/transientDocuments");
request.Method = "POST";
request.Headers["Authorization"] = $"Bearer {_bearerToken}";
request.Headers["x-api-user"] = "email:" + _apiUserEmail;

var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
request.ContentType = $"multipart/form-data; boundary={boundary}";

using (var requestStream = request.GetRequestStream())
{
var fileHeader = Encoding.ASCII.GetBytes($"\r\n--{boundary}\r\nContent-Disposition: form-data; name=\"File\"; filename=\"{Path.GetFileName(documentPath)}\"\r\nContent-Type: application/octet-stream\r\n\r\n");
requestStream.Write(fileHeader, 0, fileHeader.Length);

var fileBytes = File.ReadAllBytes(documentPath);
requestStream.Write(fileBytes, 0, fileBytes.Length);

var boundaryBytes = Encoding.ASCII.GetBytes($"\r\n--{boundary}--\r\n");
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
}

using (var response = (HttpWebResponse)request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
var responseContent = reader.ReadToEnd();
var transientDocumentId = JObject.Parse(responseContent)["transientDocumentId"].ToString();
transientDocumentIds.Add(transientDocumentId);
}
}
}

// Create agreement without form fields
var agreementRequest = (HttpWebRequest)WebRequest.Create($"{_apiAccessPoint}/api/rest/v6/agreements");
agreementRequest.Method = "POST";
agreementRequest.Headers["Authorization"] = $"Bearer {_bearerToken}";
agreementRequest.Headers["x-api-user"] = "email:" + _apiUserEmail;
agreementRequest.ContentType = "application/json";

var fileInfos = transientDocumentIds.Select(id => new { transientDocumentId = id }).ToArray();

var agreementBody = new
{
fileInfos = fileInfos,
name = "FC Agreement " + pintProcessInstanceID.ToString() + "-" + currentTimeOnly.ToString(),
participantSetsInfo = new[]
{
new
{
memberInfos = new[] { new { email = pstrToEmail } },
order = 1,
role = "SIGNER"
}
},
signatureType = "ESIGN",
state = "AUTHORING"
};

using (var agreementStream = agreementRequest.GetRequestStream())
{
var bodyBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(agreementBody));
agreementStream.Write(bodyBytes, 0, bodyBytes.Length);
}

string agreementId;
using (var agreementResponse = (HttpWebResponse)agreementRequest.GetResponse())
using (var reader = new StreamReader(agreementResponse.GetResponseStream()))
{
var agreementResponseContent = reader.ReadToEnd();
agreementId = JObject.Parse(agreementResponseContent)["id"].ToString();
}

// Use PUT request for form fields with new schema
var formFieldsRequest = (HttpWebRequest)WebRequest.Create($"{_apiAccessPoint}/api/rest/v6/agreements/{agreementId}/formFields");
formFieldsRequest.Method = "PUT";
formFieldsRequest.Headers["Authorization"] = $"Bearer {_bearerToken}";
formFieldsRequest.Headers["x-api-user"] = "email:" + _apiUserEmail;
formFieldsRequest.ContentType = "application/json";

var fields = new List<object>();
for (int i = 0; i < transientDocumentIds.Count; i++)
{
int numPages = pageCounts[i];
for (int page = 1; page <= numPages; page++)
{
fields.Add(new
{
locations = new[]
{
new
{
height = 30,
left = 162,
pageNumber = page,
top = 100,
width = 280
}
},
name = $"Signature (Document-{i + 1}, Page-{page})",
alignment = "LEFT",
backgroundColor = "TRANSPARENT",
borderColor = "BLACK",
borderStyle = "SOLID",
borderWidth = 1,
calculated = false,
conditionalAction = new
{
action = "SHOW",
anyOrAll = "ANY",
predicates = new[]
{
new
{
fieldLocationIndex = 1,
fieldName = "test 1",
value = ""
}
}
},
contentType = "SIGNATURE",
inputType = "SIGNATURE",
readOnly = false,
required = true,
visible = true
});
}
}

var formFieldsBody = new { fields };
using (var formFieldsStream = formFieldsRequest.GetRequestStream())
{
var formFieldsBodyBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(formFieldsBody));
formFieldsStream.Write(formFieldsBodyBytes, 0, formFieldsBodyBytes.Length);
}

using (var formFieldsResponse = (HttpWebResponse)formFieldsRequest.GetResponse())
using (var formFieldsReader = new StreamReader(formFieldsResponse.GetResponseStream()))
{
var formFieldsResponseContent = formFieldsReader.ReadToEnd();
Console.WriteLine($"Form fields added: {formFieldsResponseContent}");
}

// Send the agreement for signing
var sendRequest = (HttpWebRequest)WebRequest.Create($"{_apiAccessPoint}/api/rest/v6/agreements/{agreementId}/state");
sendRequest.Method = "PUT";
sendRequest.Headers["Authorization"] = $"Bearer {_bearerToken}";
sendRequest.ContentType = "application/json";

var sendBody = new { state = "IN_PROCESS" };
using (var sendStream = sendRequest.GetRequestStream())
{
var sendBodyBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sendBody));
sendStream.Write(sendBodyBytes, 0, sendBodyBytes.Length);
}

using (var sendResponse = (HttpWebResponse)sendRequest.GetResponse())
using (var sendReader = new StreamReader(sendResponse.GetResponseStream()))
{
var sendResponseContent = sendReader.ReadToEnd();
Console.WriteLine($"Agreement sent for signing: {sendResponseContent}");
}

return agreementId;
}
catch (Exception ex)
{
return "Error:" + ex.Message;
}
}

 

TOPICS
How to sign , Manage documents , Send documents
791
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Nov 19, 2024 Nov 19, 2024

Struggling with this API as well!

I am assuming you tried debugging this to ensure the agreementId value you obtained was received and extracted correctly, and not blank?

 

I did discover that you need to set the assignee property when you create the form fields. This is a unique ID assigned to the first participant who is signing. You can only get this by using a GET to download the agreement after you created it. It only gets set if you set the status to AUTHORING (which you do). Not sure if this is the correct approach in the absence of any documentation about this.

 

I then run into the issue that when I call the state PUT method to change to IN_PROCESS I get an 'OPERATION_NOT_ALLOWED' (despite the document going out??!).

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Nov 19, 2024 Nov 19, 2024

Signature fields indeed need the assignee property to be used. Regular fields don't need to be assigned.
You need the participantSetID which you can get by calling GET/agreements/{agreementId}/members
A successful PUT call moves the agreement to IN_PROGRESS, no need to call the state end point.

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Nov 21, 2024 Nov 21, 2024

Aaaaah..!!!

Wish this was DOCUMENTED or shown in an example using FormFields somewhere! Finally makes sense.

Feedback for your dev team on my experience:

1. Your Swagger JSON is out of date (v1.2). Swagger 2.0 was ten years ago, and it's now OpenAPI.

2. Upgrading it to v2 or v3 shows that it's also invalid: some definitions are missing or differ in casing. I had to manually edit the agreements.json to get it to work.

3. Documentation/examples do need to be updated to be more helpful.

4. A single POST that uploads, creates and sends an Agreement would be a good idea. As it is I have to execute four API calls to send a single agreement if I used form fields, rather than the generators.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Nov 21, 2024 Nov 21, 2024

FWIW the Api reference hsa been updated to OAS 3.1 

Using the API to add form fields is imo the most difficult way, you can also rely on text tag markup in the uploaded documents, or make use of library templates.


One way to make your own sample is to use the Sign webportal to create an agreement and use the drag and drop form tool to add any form field you need and to send for signature and then use 

GET /agreements/{agreementId}/formFields
to get a sample json.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Nov 21, 2024 Nov 21, 2024

Thanks for the reply.. I'm confused how I never managed to find the OAS 3.1 versions! Was this a recent change?

It's still invalid though: try passing that URL to this

https://validator.swagger.io/#/Validator/validateByUrl

https://validator.swagger.io/validator/?url=https%3A%2F%2Fsecure.eu1.echosign.com

Missing some definitions, e.g. #/components/examples.Full

 

Yes, the generated form field is the simpler option, but our API was originally built for "another" signing API and it was easy to implement fixed-location fields. We didn't use anchor-text placement, alas.

 

We can't pre-create the documents as they are dynamically created by the code, hence the need to add form fields.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Nov 21, 2024 Nov 21, 2024
LATEST

It was updated only last week. 

 

As for documentation generation, text tags are just text, which you may be able to insert to the document when  generating it, or make part of the master document template if there is one. Or instead of text tags you could insert your own anchor tags.  
Of course that may well fall under the easier said than done umbrella. 🙂

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines