UPDATED WITH WORKING SOLUTION
I'm trying to integrate the DocuSign API using ColdFusion and keep running into an issue. I am able to authenticate successfully and retrieve the base URL. However, when trying to create the envelope with a single recipient and document, I get an error stating that the "boundary terminator" is not found in the request (when it clearly is).
I know this questions is similar to another post here but that was from a while ago and was never completely answered.
I first read in the PDF document:
<cffile action="READBINARY" file="Agreement.pdf" variable="docData">
<cfset docData = BinaryEncode(docData,"Base64")>
I then create the envelope definition in XML using the element to include the encoded binary PDF data:
<cfset envelope = "<envelopeDefinition xmlns=""http://www.docusign.com/restapi"">
<status>Sent</status>
<emailSubject>eSignature request</emailSubject>
<emailBlurb>Please sign the document</emailBlurb>
<recipients>
<signers>
<signer>
<recipientId>1</recipientId>
<name>eCS Buyer</name>
<email>XXX#XXXX.com</email>
<tabs>
<signHereTabs>
<signHere>
<documentId>1</documentId>
<pageNumber>3</pageNumber>
<xPosition>10</xPosition>
<yPosition>100</yPosition>
</signHere>
</signHereTabs>
</tabs>
</signer>
</signers>
</recipients>
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
<fileExtension>pdf</fileExtension>
<documentBase64>#docData#</documentBase64>
</document>
</documents>
</envelopeDefinition>">
Lastly, I POST the information:
<cfhttp url="#baseURL#/envelopes" method="POST" resolveurl="Yes" throwonerror="No">
<cfhttpparam name="X-DocuSign-Authentication" type="HEADER" value="<DocuSignCredentials><Username>#userName#</Username><Password>#password#</Password><IntegratorKey>#integratorKey#</IntegratorKey></DocuSignCredentials>">
<cfhttpparam name="Content-Type" type="HEADER" value="application/xml">
<cfhttpparam name="Accept" type="HEADER" value="application/xml">
<cfhttpparam name="Content-Length" type="HEADER" value="#Len(envelope)#">
<cfhttpparam name="request_body" type="BODY" value="#envelope#">
</cfhttp>
I tried changing the TYPE attribute for the request body to XML and FORMFIELD but is still doesn't work. I even tried changing the envelope to JSON format to no avail.
The error that is generated is:
<errorCode>INVALID_REQUEST_BODY</errorCode><message>The request body is missing or improperly formatted. The XML request does not match the expected format. </message></errorDetails>
I have been struggling with this for weeks. Any guidance would be GREATLY appreciated.
Most likely an issue with extra CRLF (i.e., extra line breaks). See the answer in this other forum post for an example of what the full request structure should look like: Docusign : Unable to create envelope from document in restapi v2.
UPDATE #1
Looks like your XML request body is missing information about the Document. Try adding a documents element as a child element of envelopeDefinition (i.e., as a peer element to recipients):
<envelopeDefinition xmlns=""http://www.docusign.com/restapi"">
...
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
</document>
</documents>
...
</envelopeDefinition>
Also, make sure the document bytes that your sending in the subsequent part of the request are not encoded.
UPDATE #2
I don't know much about ColdFusion, but this line of your code makes it look like you're (base64) encoding the byte stream that you're including in the Request:
<cfset docData = BinaryEncode(docData,"Base64")>
This could be causing your latest issue, as I don't believe DocuSign will accept an encoded byte stream when it's included in the manner you're currently utilizing.
If you must base64-encode the byte stream, you could add a documentBase64 property under the document element to contain the base64-encoded byte stream. (See page 104 of the DocuSign REST API Guide -- http://www.docusign.com/sites/default/files/REST_API_Guide_v2.pdf.) If you utilized this approach, your request would no longer need to be a 'multipart' request, since the document bytes would be included in the XML portion of the Request Body (not in a subsequent/separate part). Here's an example of what the Request would look like, if the base64-encoded byte stream was included within the documentBase64 element:
POST /restapi/v2/accounts/ACCOUNT_NUMBER/envelopes HTTP/1.1
Host: demo.docusign.net
X-DocuSign-Authentication: {"Username":"SENDER_EMAIL_ADDRESS","Password":"PASSWORD","IntegratorKey":"INT_KEY"}
Content-Type: application/xml
<envelopeDefinition xmlns="http://www.docusign.com/restapi">
<accountId>ACCOUNT_ID</accountId>
<status>sent</status>
<emailSubject>eSignature request</emailSubject>
<emailBlurb>Please sign the document</emailBlurb>
<recipients>
<signers>
<signer>
<email>johnsEmail#outlook.com</email>
<name>John Doe</name>
<recipientId>1</recipientId>
<routingOrder>1</routingOrder>
<tabs>
<signHereTabs>
<signHere>
<documentId>1</documentId>
<pageNumber>1</pageNumber>
<xPosition>10</xPosition>
<yPosition>100</yPosition>
</signHere>
</signHereTabs>
</tabs>
</signer>
</signers>
</recipients>
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
<fileExtension>pdf</fileExtension>
<documentBase64>BASE64-ENCODED-BYTE-STREAM</documentBase64>
</document>
</documents>
</envelopeDefinition>
Since the document bytes are included within the XML portion of the request, the request no longer needs to be multi-part -- simply pass the XML Request body as I've shown above, and that's it.
The error is most likely due to the extra CRLF you have after the document bytes and before your closing boundary --MYBOUNDARY--. You have this:
#docData#
--MYBOUNDARY--
Try changing that to this without that extra CRLF:
#docData#
--MYBOUNDARY--
For reference you can check out the DocuSign API Walkthrough for sending a signature request on a document. Each of the different language versions shows you how the outgoing request body should be formatted.
Related
I'm trying to integrate reCaptcha (v3) to a ColdFusion site. I'm not too hot on the CF syntax and currently I'm seemingly getting nothing back from the verify request on the server side.
Can anyone see anything obviously wrong and/or point me in the right direction please?
Client Side:
<script src='https://www.google.com/recaptcha/api.js?render=6..."></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('6...', {action: 'contact'})
.then(function(token) {
$("#recaptchaToken").val(token);
});
});
</script>
I've got a hidden field recaptchaToken in my form and I can see the token value going in to it.
Server Side:
<cfhttp
url="https://www.google.com/recaptcha/api/siteverify"
method="POST"
result="captchaResponse">
<cfhttpparam
type="formfield"
name="secret"
value='6...'
/>
<cfhttpparam
type="formfield"
name="response"
value='#form.recaptchaToken#'
/>
</cfhttp>
<cfdump var=#captchaResponse.filecontent# />
I'm getting a red box output titled object of java.io.ByteArrayOutputStream
I've tried to dump both captchaResponse and captchaResponse.filecontent to no avail.
I'm expecting data in the form of:
{
"success": true|false, // whether this request was a valid reCAPTCHA token for your site
"score": number // the score for this request (0.0 - 1.0)
"action": string // the action name for this request (important to verify)
"challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
"hostname": string, // the hostname of the site where the reCAPTCHA was solved
"error-codes": [...] // optional
}
Update
The solution seems to be as Alex suggested below:
<cfdump var=#toString(captchaResponse.filecontent)# />
This gives me a JSON string in the format expected so I can convert this to an object and complete the verification.
Whenever cfhttp is not sure how to treat a response, the raw content stays untouched and is kept as Byte array. This usually indicates that the Content-Type header is not specified by the responding server or the content was only partially retrieved.
To force a string representation of the content, you can use toString() to convert the raw Byte array, e.g. toString(captchaResponse.filecontent). The function is quite robust and can also handle already converted strings, so it is usually safe to use.
However, there is something else to be aware of here. When using cfhttp without setting the throwOnError attribute to true (the default value is false), failed HTTP requests will still return a result, a crippled result. The struct will not contain the fileContent key and thus cause an exception at runtime. You might want to add error handling here, in case https://www.google.com/recaptcha/api/siteverify is not reachable or the accepted TLS protocol is not supported by your JRE. We had this issue with SNI and TLS 1.2 with a former version of ColdFusion, namely 8. Be warned.
I found a web-site that clears exif data from an image. The source can either be an uploaded picture or a URL. I thought, perhaps, I could use this with CFHTTP to do this automatically for pictures I post to my web-site. I know I can probably run my images manually through this site before I upload them to my site. Call this an exercise if you want.
Here is the code I am using, which basically matches the form source on this very simple web-site (link)
<cfhttp method="POST" url="https://www.verexif.com/en/quitar.php" result="result" >
<cfhttpparam name="foto_url" type="formfield" value="{myimageurl}">
</cfhttp>
When I CFDUMP the result, I get the following:
When I try to use DeserializeJSON(result.Filecontent), it gives me a ColdFusion error:
When I url-encode my original URL in the CFHTTP tag, the result.filecontent contains the source code of the original web-site.
As can be seen in the first image above, there is a file called 'foto_no_exif.jpg' included in the output. This is the file I need to download. How can I do this ?
In your current dump, you have the modified image, but you need to get to accesses it as binary data. You can force the file content of the request to be treated as binary data by adding the attribute getasbinary to your cfhttp tag.
Working example:
<cfset imageURL ='https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/long_description.jpg'/>
<cfhttp method="get" getasbinary="yes" charset="utf-8" url="https://www.verexif.com/en/quitar.php" result="result">
<cfhttpparam name="foto_url" type="formfield" value="#imageURL#">
</cfhttp>
<cfcontent variable="#result.Filecontent#" type="image/jpg" reset="true" />
Run it on TryCF.com
Twilio newbie here.
I have a Twilio voice application that collects a bunch of data (international topup sales) - and there is a point where the actual process of purchasing the topup takes place.
This process can last anywhere from 10 to 30 seconds, where most of them are about 15 seconds. Sounds to me like I need to use the Twilio <ENQUEUE> tag (https://www.twilio.com/docs/voice/twiml/enqueue), but it does not work.
I am simply calling it like this (happens to be ColdFusion):
<Enqueue
waitUrl="processtopup.cfm"
method="POST"
action="topupdone.cfm">processTopup</Enqueue>
Within the processtopup.cfm file is the <PLAY> tag (which won't work because that is the page that takes more than 15 seconds.
Sorry - but I'm just confused on ho this should work. Thanks in advance!
Here is a possible solution. I've tested this and it works.
The main idea is to play some message/music in a loop until ColdFusion does the job, then, when ColdFusion is done, instruct the call to execute a different Twilio XML by making a POST request to Twilio's API call resource.
When a call comes in, and Twilio hits your endpoint, capture the call id, it will be used to switch the call to a different XML. The call id it's passed as FORM.CALLSID or URL.CALLSID depending on your webhook configuration at Twilio.
The call id looks something like CAdecbfa7e8e2a9d09336abcd57044cf74.
Pass the call id trough your flow (as url parameter should be fine) so it reaches processtopup.cfm.
Move the long running code from processtopup.cfm to let's say
processtopup-action.cfm
Code in processtopup.cfm should now return immediately XML for playing loop (or you can play some .mp3), I'm showing with a message:
<cfoutput><?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say loop="0">Please wait while we process your request...</Say>
</Response>
</cfoutput>
<cfhttp
url="http://www.yourwebsite.com/processtopup-action.cfm?callsid=#FORM.CALLSID#"
method="get"
timeout="1" />
The code for processtopup-action.cfm
<!--- // place your long running code here --->
<cfset accountSid = '{your account sid}' />
<cfset authToken = '{your auth token}' />
<cfhttp
url="https://api.twilio.com/2010-04-01/Accounts/#variables.accountSid#/Calls/#URL.CALLSID#.json"
method="POST"
username="#variables.accountSid#"
password="#variables.authToken#"
result="http_result">
<cfhttpparam
name="Url"
value="http://www.yourwebsite.com/finish.cfm"
type="formfield" />
</cfhttp>
Code for finish.cfm
<cfoutput><?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>This is the data you're looking for.</Say>
<Say>Thank you. Goodbye!</Say>
<Hangup />
</Response>
</cfoutput>
Of course, you can pass other parameters as you need.
Again, the main idea is that processtopup-action.cfm, after executing your long running code, makes a POST request to Twilio's API and instructs the call to switch to execute new TwiML located at http://www.yourwebsite.com/finish.cfm
Docs:
Call Redirection via the Twilio REST API
(https://www.twilio.com/blog/2009/09/call-queueing-putting-callers-on-hold-calll-redirect-new-url-new-feature.html)
Modifying Live Calls
(https://www.twilio.com/docs/voice/modify-live-calls)
I am working on a ColdFusion app that authenticates to SharePoint Online and pulls some files using SharePoint's REST API as described in http://paulryan.com.au/2014/spo-remote-authentication-rest/
When I try to obtain the FormDigestValue by posting to _api/contextinfo if I set the cfhttpparam type to cookie I get a 403 forbidden, but if I pass the cookies as a header everything works but I don't understand why.
<cfhttpparam
type="header"
name="cookie"
value="rtFa=#rtFa#;FedAuth=#FedAuth#"
/>
Works but
<cfhttpparam
type="cookie"
name="rtFa"
value="#rtFa#"
/>
<cfhttpparam
type="cookie"
name="FedAuth"
value="#FedAuth#"
/>
Fails
The best way to see what is happening would be to inspect the traffic and see what is happening. Other than that, I know that the difference between using the header type and the cookie type for the cfhttpparam tag is URL encoding.
When you use the header type the value is not URL encoded.
When you use the cookie type the value is URL encoded.
So my guess would be that their API does not like when the value is URL encoded.
Document reference for cfhttpparam attributes.
I am using the following code to call the docusign api. I got the information from this link.
<soap:Body>
<ns:CreateEnvelopeFromTemplates>
<ns:TemplateReferences>
<ns:TemplateReference>
<ns:TemplateLocation>Server</ns:TemplateLocation>
<ns:Template>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:Template>
<ns:RoleAssignments>
<ns:RoleAssignment>
<ns:RoleName>Company</ns:RoleName>
<ns:RecipientID>1</ns:RecipientID>
</ns:RoleAssignment>
</ns:RoleAssignments>
</ns:TemplateReference>
</ns:TemplateReferences>
<ns:Recipients>
<ns:Recipient>
<ns:ID>1</ns:ID>
<ns:UserName>Fred Flintstone</ns:UserName>
<ns:Email>fred.flintstone#...</ns:Email>
<ns:Type>Signer</ns:Type>
<ns:RoleName>Caveman</ns:RoleName>
<ns:RoutingOrder>1</ns:RoutingOrder>
</ns:Recipient>
</ns:Recipients>
<ns:EnvelopeInformation>
<ns:AccountId>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:AccountId>
<ns:EmailBlurb>This Envelope was sent through the DocuSign API using ColdFusion</ns:EmailBlurb>
<ns:Subject>DocuSign it! using ColdFusion</ns:Subject>
</ns:EnvelopeInformation>
<ns:ActivateEnvelope>true</ns:ActivateEnvelope>
</ns:CreateEnvelopeFromTemplates>
</soap:Body>
I am unable to understand what the RoleAssignment tag under Template is used for. I have gone through the documentation here but did not understand it.
I think this is the only reason I am not getting a response. I commented this portion in my code but I am getting the following output after changing all of the credentials.
An error occurred!
struct
Charset [empty string]
ErrorDetail Unknown host: demo.docusign.net
Filecontent Connection Failure
Header [empty string]
Mimetype Unable to determine MIME type of file.
Responseheader
struct [empty]
Statuscode Connection Failure. Status code unavailable.
Text YES
Can anyone please help me out with this?
I don't think the RoleAssignment has anything to do with that particular error. You need to combine the SOAP Header and the SOAP Body from their example. The SOAP Body should be contained with the SOAP Header block.
Notice that their example for the SOAP Header contains the <soap:Body> block. Then their example for the SOAP Body is also surrounded by the <soap:Body> block. They should be combined like this for your <cfhttp> call:
<cfset UserName = "[XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX]XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX" />
<cfset Password = "SuperSecret" />
<cfscript>
strNonce = ToBase64(createUUID());
</cfscript>
<!--- create the request body XML block --->
<cfsavecontent variable="request_xml">
<cfoutput>
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="http://www.docusign.net/API/3.0">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-1" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>#UserName#</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0##PasswordText">#Password#</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0##Base64Binary">#strNonce#</wsse:Nonce>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<ns:CreateEnvelopeFromTemplates>
<ns:TemplateReferences>
<ns:TemplateReference>
<ns:TemplateLocation>Server</ns:TemplateLocation>
<ns:Template>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:Template>
<ns:RoleAssignments>
<ns:RoleAssignment>
<ns:RoleName>Company</ns:RoleName>
<ns:RecipientID>1</ns:RecipientID>
</ns:RoleAssignment>
</ns:RoleAssignments>
</ns:TemplateReference>
</ns:TemplateReferences>
<ns:Recipients>
<ns:Recipient>
<ns:ID>1</ns:ID>
<ns:UserName>Fred Flintstone</ns:UserName>
<ns:Email>fred.flintstone#...</ns:Email>
<ns:Type>Signer</ns:Type>
<ns:RoleName>Caveman</ns:RoleName>
<ns:RoutingOrder>1</ns:RoutingOrder>
</ns:Recipient>
</ns:Recipients>
<ns:EnvelopeInformation>
<ns:AccountId>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</ns:AccountId>
<ns:EmailBlurb>This Envelope was sent through the DocuSign API using ColdFusion</ns:EmailBlurb>
<ns:Subject>DocuSign it! using ColdFusion</ns:Subject>
</ns:EnvelopeInformation>
<ns:ActivateEnvelope>true</ns:ActivateEnvelope>
</ns:CreateEnvelopeFromTemplates>
</soap:Body>
</soap:Envelope>
</cfoutput>
</cfsavecontent>
(This example was taken directly from their documentation. I only combined the two blocks.)
Then send that request (from their example):
<!--- send the request to the DocuSign service --->
<cfhttp url="https://demo.docusign.net/api/3.0/api.asmx" method="post" result="http_response">
<!--- make sure you set this correctly for the call you are making --->
<cfhttpparam type="header" name="SOAPAction" value="http://www.docusign.net/API/3.0/CreateEnvelopeFromTemplates" />
<cfhttpparam type="header" name="accept-encoding" value="no-compression" />
<cfhttpparam type="xml" value="#trim(request_xml)#" />
</cfhttp>
And finally handle the response (from their example):
<!--- if we received a successful response --->
<cfif find("200", http_response.statusCode)>
<cfscript>
response_xml = xmlParse(http_response.fileContent);
// use XPath to get the top level element you want from the SOAP response
result_node = xmlSearch(response_xml, "//*[local-name() = 'CreateEnvelopeFromTemplatesResult']")[1];
// use dot notation to navigate the XML structure
envelope_id = result_node.EnvelopeID.xmlText;
</cfscript>
<cfoutput>
EnvelopeID: #envelope_id#
</cfoutput>
<cfelse>
<cfoutput>
An error occurred!
</cfoutput>
<cfdump var="#http_response#">
</cfif>
If this is a POST request (submitting data), it looks as though the webservice is not recognising the submission of the actual SOAP XML file.
Are you sending the XML as an XML type as shown in the documentation?
<cfhttpparam type="xml" value="#trim(request_xml)#" />
The RoleAssignment tags are used for assigning your recipient to a particular template role. When you send an envelope that uses a template, you are referencing a template that you have created through the DocuSign console (demo.docusign.net). When you created the template, you had to specify at least one template role for the template, which contains a role name, recipient name, and recipient email. The value you use for the role here needs to match the role name you provide in the api call. So if you called your role "Signer1" for instance, you need to have
<ns:RoleName>Signer1</ns:RoleName>
If you named the role "Company" the keep like you have it.