FIX: SharePoint 2013 Workflow recursion prevention – Part 2
Following FIX: SharePoint 2013 Workflow recursion prevention – Part 1, this post will walk you through the processes of designing your workflow that creates a list item on another list and invokes the workflow associated with that list using the new REST APIs released through SharePoint 2013 May 2014 CU as pointed out in part 1 of this series. If you haven’t read through part 1 of this series I suggest you do to get some background that will help you appreciate this post.
The scenario:
List1 has a workflow Workflow1 associated with it.
Workflow1 can be set to auto-start on item adding/updating or set to manually start or both, it doesn’t matter.
Workflow1 is designed to create a list item on List2.
List2 has a workflow Workflow2 associated with it.
Workflow2 should have manually start workflow option enabled. (Allow this workflow to be manually started)
Workflow2 simply logs the title of the list item in workflow history list.
NOTE: Both Workflow1 and Workflow2 are built on 2013 workflow platform.
Publish both the workflows and create an item on List1. Manually start Workflow1 on the list item and you should see that it completes successfully.
Now browse to List2 and you will see that a new list item was successfully created by Workflow1.
The problem:
But Workflow2 would never start off. Refer part 1 of this series, check ULS logs and you’ll see Workflow2 didn’t start because of workflow recursion prevention.
Fix:
The fix is to leverage the new REST APIs exposed to start off Workflow2 from Workflow1. Let’s open Workflow1 in SharePoint Designer’s workflow editor.
We drop a “Build Dictionary” action just above the create item in list action with the following name/value pairs of type string.
accept: application/json;odata=verbose
content-type: application/json;odata=verbose
Choose the dropdown against “(Output to Variable: dictionary)” and create a variable to type Dictionary named “ReqHeader”.
Choose the dropdown against “(Output to Variable: create)” and create a new variable named “NewItemGuid”. It will be of type GUID.
Drop a “Call HTTP Web Service” action after the create item in list action.
Click “this”, use the … button against the textbox titled “Enter the HTTP web service URL”. This will bring up string builder dialog box.
Click “Add or Change Lookup”, change “Data source” dropdown to select “Workflow Context” and for “Field from source” choose “Current Site URL” and hit OK.
Append /_api/web/lists/GetByTitle('List2')/items?$filter=GUID eq ' to the end of the current site URL token (watch out for the single open quote in the end, that is necessary too).
Click “Add or Change Lookup” again, change “Data source” dropdown to select “Workflow Variables and Parameters” and then choose “Variable: NewItemGuid” for “Field from source”. Hit OK.
The string builder dialog will now have the following string in it.
We are calling the REST method GetByTitle passing it the title of the list we are interested in (for this scenario it’s List2). We are then asking for all the items filtered on the specific GUID which is the GUID of the item we just created in the create item in list action.
Hit OK on string builder dialog. The “Call HTTP Web Service” dialog should now look like this. Ensure you select “HTTP GET” for the HTTP method. And hit OK.
Choose “Properties” option from the context menu of “Call HTTP Web Service” action.
Set “RequestHeaders” to the request header variable you created and create a new variable of type dictionary named “RespContent” for “ResponseContent” as shown below. Hit OK.
Drop a “Get an Item from a Dictionary” action after “Call HTTP Web Service” action and click “item by name or path” hyperlink. In the editor type d/results(0) .
Click the dictionary hyperlink and choose “Variable: RespContent” from the dropdown. Click the item hyperlink and again choose “Variable: RespContent” from the dropdown. End result will be.
There are plenty of articles out there that talk about how to parse the JSON to get to the actual data we need so I am not going to cover that here. But remember we can use Fiddler to determine the structure of JSON result and we’ll use that knowledge to decide on how we will get to the result we need (for e.g., d/results(0)).
What we did above is that we want the value at d/results(0) from the RespContent dictionary variable we get back from the web service call. We are then storing the result again back in RespContent because the result is also of type dictionary.
Drop another “Get an Item from a Dictionary” action. Click on “item by name or path” hyperlink and type Id. Click “dictionary” hyperlink and choose “Variable: RespContent”. Click the item hyperlink in “(Output to item)” and create a new variable named “NewItemId” of type integer as shown below.
We did all these to get the ID (the sequence number) value of the list item we just created. Phew!!! This is because we need to know the ItemID of the list item we just created as we need to pass it to the StartWorkflowOnListItemBySubscriptionId REST method later. But we are just 30% into our design
The other parameter that StartWorkflowOnListItemBySubscriptionId needs is the workflow SubscriptionId.
We drop another “Call HTTP Web Service” action and construct our next REST call. This time we call /_api/web/lists/GetByTitle('List2') . We set the same “ReqHeader” variable to this call. And set the same “RespContent” variable as we did before. The end result you should have in “Call HTTP Web Service” dialog is shown below.
And in designer we should see.
We then add another “Get an Item from a Dictionary” action. Ask for d/Id from “RespContent” variable and assign the output to a new variable named “List2Guid” of type GUID as shown below.
Now the variable “List2Guid” will have the GUID of List2. We need this to get the workflow subscriptionId that we can get by calling an already available REST API called /_api/SP.WorkflowServices.WorkflowSubscriptionService.Current/EnumerateSubscriptionsByList.
We now drop another “Call HTTP Web Service” action and set it up get all workflow subscriptions. Here’s how string builder should look like for this call.
And we set the “HTTP method” option for this “Call HTTP Web Service” action to “HTTP POST” as shown below.
We should now have the workflow design as shown below.
Now we have a tricky situation where we need to get the subscriptionId of the exact workflow that we are interested to start off. This is where Fiddler is very helpful. Again I’ll defer to the plethora of articles available online that tells us how to do a GET, POST REST calls using Fiddler. But I am going to rush on this piece a bit.
Through Fiddler (by submitting a POST request to the same REST method EnumerateSubscriptionsByList, we see the following output.
Workflow2 happens to be the 3rd in the list of workflows associated to List2 so I can get that out using the 2nd index. So we drop two more “Get an Item from a Dictionary” actions. In the first action we ask for d/results(2) from “RespContent” and we store the output again in “RespContent”. In the second action we ask for Id which happens to be the SubscriptionId of Workflow2 on list List2 from “RespContent” again and store the result in a new variable named “SubscriptionId” of type GUID. Our workflow design should now look like below.
Now we are at the final stage where we will start the workflow.
Drop our last “Call HTTP Web Service” action and call the new /_api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId REST method by passing in the SubscriptionId and NewItemId variables. Our string builder dialog should look like the below. (Ensure the parameter names of this method is exactly as it is shown below and that the values are enclosed within single quotes).
This will again be a POST request.
After setting the “ReqHeader” correctly using the “Properties” context menu option for the last “Call HTTP Web Service” action, we drop a “Set Workflow Status” action. The completed Workflow1 should look like below.
That’s it!!
We Save/Publish our workflow, go to List1 and create a new item. Start off Workflow1 manually on that new item. It should complete successfully. Then we go to List2 and we should see that a new item is created and Workflow2 was trigged off too.
If we click on “Stage 1”, we’ll see that Workflow2 logged the message as we designed.
I hope the information in this post was useful. Please check out the next part SharePoint 2013 Workflow recursion prevention – Part 3 to achieve the above objective from a Visual Studio SharePoint 2013 Workflow.
Comments
Anonymous
October 08, 2014
I my common scenario I use the Copy item action in my SP 2013 WF. This action does not have the Output to variable option. Any known solution for the unique ID to be used in the Webservice?Anonymous
December 03, 2014
Last call is not working for me. No idea what could be the problem.Anonymous
December 04, 2014
@Anitha: You should have SharePoint 2013 May CU 2014 installed (15.0.4605.1004) to use the new APIs.Anonymous
December 04, 2014
Getting 403 forbidden error with the above steps. Resolved the error using fiddler (added X-request digest value) while executing the post call. But similar process is not achievable using SharePoint designer 2013. Any help appreciated.Anonymous
December 07, 2014
Hi, I am working on office 365 site.how can i install the CU 2014? any work around for this scenario?Anonymous
December 08, 2014
@Chandrakant - It would help if you could give more details on which particular part is failing and with what error. @Anitha - This should work in O365. You can take a look at the endpoint APIs by using /_api/$metadata and should be able to see the new APIs in the output. Any info on what's the error you are hitting would help.Anonymous
December 08, 2014
Hi, I am not getting any error.But the last call is not triggering the second workflow. I am using SharePoint designer 15.0.4420.1017 version.Anonymous
January 04, 2015
@Anitha: A Fiddler trace should help you track down this problem you are experiencing.Anonymous
January 14, 2015
getting a 403 error when trying to run this : _api/SP.WorkflowServices.WorkflowSubscriptionService.Current/EnumerateSubscriptionsByList any ideas?Anonymous
January 21, 2015
I used this great article to solve a similar problem. However when I try to post the following data (using postman and Office 365): _api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId(subscriptionId='E2DE0A7A-A119-4B4D-863D-90F528000949',itemid='5d09e0bf-1f4f-49f2-a7cc-d1ec81acdaa5') including the headers: X-RequestDigest, content-type and accept I get the following result: { "error": { "code": "-1, Microsoft.SharePoint.Client.InvalidClientQueryException", "message": { "lang": "en-US", "value": "Cannot handle the data at position 0." } } } I'm only using the last step from your solution. I gathered the subscriptionID and itemID by right clicking the link for the manual trigger for the workflow and copying that shortcut. Not sure if that is the correct data? The item ID is the same as when I use the workflow to collect it, so that should be ok. I tried gathering the subscription ID in the same way as in your article, but that gives a similar problem: GET _api/web/lists/GetByTitle('listname') -> this works, I then take the ID field to build the next query: POST _api/SP.WorkflowServices.WorkflowSubscriptionService.Current/EnumerateSubscriptionsByList(guid'768e9d10-2bd3-44b0-bf05-81f1fabcbd39')?select=id and I get the same error; Cannot handle the data at position 0 Hopefully you see what I'm doing wrong here? Thank YouAnonymous
February 05, 2015
Great post is really a lifesaver, I would like to share an different step that I made. Instead of call EnumerateSubscriptionsByList, I made another REST request using /_api/web/lists/getbytitle('wfsvc')/items?$filter=WSDisplayName eq 'WFNAME' where WFNAME is the name of the workflow and in the response I get d/results(0)/WSGUID which is the subscriptionId. I did this because the call to Enumerate doesn't worked, I hope it helps someone.Anonymous
April 03, 2015
David, on the off chance that you happen to see this, I believe that the itemId is supposed to be the actual "ID" of the item in the list, so an integer rather than a GUID. Hope that helps you out.Anonymous
April 03, 2015
Now my turn to ask a question! ;) Everything in this article is amazing...well documented, easy to follow, outstanding... got me 75% of the way toward solving an issue I've been noodling out... I say 75% because the final web service call doesn't seem to work in my environment. The ResponseStatusCode that comes out of the WF isn't terribly helpful (NotFound), so I used a REST client to feed the web services calls one by one and get the feeder data bits needed to perform the last step... When I get the last step, I provide the following POST call to https://myServer/sites/mySiteCollection/mySite/_api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId(subscriptionId='9a2a8e16-721e-4ebf-95b0-73cac5065b57',itemId='18') with the appropriate headers (including X-RequestDigest, which I generate just prior to this call to ensure security context is good), and I get the following error: "Cannot find the resource for the request SP.WorkflowServices.WorkflowInstanceService.Current." Curious, I dug around a bit on ye olde interwebs and found that the only place that Google/Bing can find that calls this method in this manner is...this blog entry. Anyway, I hope you have some ideas for me, or, at the very least, can point me to the appropriate documentation for the WorkflowServices object... (I found extremely detailed and helpful documentation for Lists and List Items, Fields, Users, Groups and Roles, Webs, and User Profiles, but the WorkflowServices is off in a much, much less helpful section of MSDN with an arcane user interface that me, as a novice developer, has all kinds of trouble making heads or tails of...) Lastly, I wanted to offer up a couple of minor enhancements to the code sample:
- In the 1st and 3rd web service call, you extract the target data out of the ResponseCode dictionary by parsing the dictionary twice... to save a step there, I get the item d/result(#)/Id out of the original dictionary and push it directly to a string value. Probably saves whole microseconds... ;)
- In all of your web services calls, you build the URL string by using the Current Item URL from the Workflow Context object, followed by a "/", followed by "_api...". That "/" is superfluous since the Current Item URL is stored with a trailing "/" already. Thanks again!
Anonymous
April 05, 2015
FYI, I sorted out the reason for things not working... customer told me they had the September 2014 CU installed... for some reason I actually believed them... silly me... Once I confirmed the WF worked on an entirely different system (where I actually have farm admin rights and such), I dug a bit deeper and found that they were only running SP1... Entire day wasted, but, hey, I guess that's why I get paid the big bucks... Thanks again for the great guide!Anonymous
April 07, 2015
Thank you so much for this great post.Anonymous
April 25, 2015
This looks really useful - but before trying it out, can you confirm whether the workflows are asynchronous? I want my Workflow 1 to wait until Workflow 2 completes before continuing its Workflow 1 actions. Would like to be sure this will happen before I start trying out this solution!Anonymous
July 09, 2015
Fantastic Post - I can successfully call another SP2013 Workflow using this approach, but I need to pass some initiation parameters to the 2nd workflow? I understand that there is an additional Payload parameter that is a dictionary, but I have been unable to add this into the call to StartWorkflowOnListItemBySubscriptionId. I tried adding it as follows: Building a dictionary - With a single element (Name: Text / Type: String / Value: Test) [%Workflow Context:Current Site URL%]/_api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId(subscriptionId='<Subscription ID>',itemId='<Item ID>',payload=[Dictionary]) When I add the payload parameter the responseCode is BadRequest Is anyone able to assist?Anonymous
July 15, 2015
I know it's been quite a while since this post, but I figured I might just pitch in anyway :-) I dont quite get why you're using a http call to get the ID of the item you just created? Normally I do it like this: Set variable Data source = List 2 Field from source = ID Find list item where GUID = NewItemGUID Since both you lists are on the same site this should Work, if they are on different sites I guess http request is the way to go :-) Now I must admit I havn't actual implemented this yet, so I might have overlooked something :-D //MAnonymous
July 16, 2015
You are a hero! Thank you so much for giving such a detailed step by step article. I forgot to set my request headers for some of the later calls so got some errors but when I realised what I'd done it worked a treat. Thank you!Anonymous
July 21, 2015
I've run into a bit of a problem here, it would seem we cannot call the "/_api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId" from within an app step. I cannot figure out which permissions a normal user need to be able to call it - I have tried all (even full control), but that does not Work either :-( Any ideas? //MAnonymous
July 22, 2015
I dug around a bit more - and it seems as long as the user has contribute permission (Actually it seems edit item permission is enough) on site level they can start the workflow using the API - however I don't want my users to be able to edit every item on the site nor do I want to make unique permission to all lists and document libraries on the site. Does anybody know where I need to grant contribute permission in order for the users to be able to start one workflow from another? //MAnonymous
August 17, 2015
Please, help me... when i calling: http://unidades/sites/CETAD/Fornecedores/_api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId(subscriptionId='0', itemId='97') i´m receiving the BAD REQUEST: ERROR 400. Why ??????Anonymous
September 23, 2015
Hello Guys! I'm getting stucked in the last HTTP Web Service call. I used the action "Log ....to the workflow history list" the "responseCode3" and the result was: "InternalServerError" in the outcome. Already validated the install of the May fix (April/June 2013 CU/updates) were installed. ¿Any suggestions?Anonymous
October 09, 2015
great stuff last call not working not found. I know its all CU issues.Anonymous
October 14, 2015
Hi Sridhar, There was a task process action in sharepoint 2010 designer workflow which is now simplified in sharepoin 2013 designer. The old actually allows you to set custom fields in task list but new one does not have tha tfunctionallity. I want to have those set up as soon as the task is created in 2013 wf. is there a way to do this ?Anonymous
October 20, 2015
I'm also stuck at the last step where the workflow history logs an "InternalServerError".. Anyone else encounter that?Anonymous
October 26, 2015
No sure if anybody is reading this anymore, but it seems there might be an unhandled bug that's causing my issue: sharepoint.stackexchange.com/.../manually-start-sharepoint-2010-2013-workflow-in-sharepoint-2013-farm //MAnonymous
October 28, 2015
This was a helpful article. Thank you. I would like to suggest a shortcut: Instead of the Create item in List2 action, use the Call HTTP web service action to POST to List2. The response content will include the Id of the newly created item allowing you to bypass the several steps which indirectly did the same.Anonymous
November 01, 2015
Great blog! The only resource i could find on net about the topic. I am able to achieve what is explained in the blog. But I am unable to find the correct REST API url to start a site workflow. Can someone please help me with this.Anonymous
November 04, 2015
The comment has been removedAnonymous
December 08, 2015
Use the parameters below, in the Request Header: Name: Accept Type: String Value: application/json;odata=verbose Name: Content-Type Type: String Value: application/json;odata=verbose *Cannot use space best regards Marco Aurélio GerônimoAnonymous
March 11, 2016
Awesome! Worked the first time.. I have been looking for a solution to this problem for days! Thank you!