Why there's nothing to return
An orchestration built on the Workday Business Process template starts with Business Process Trigger and ends with Orchestration End. The business process passes data in through the Orchestration Service step's key and value grid. Workday documents no way to pass data back out to the business process. So "return the results to the next step" really means: store the results where the next step's page can read them, and make sure the next step doesn't open before they exist.
For an Extend business process, the place to store them is the app's own business objects. The approval or action page already knows the event, and from the event it can reach the target record and anything linked to it.
Model a result object linked to the target
Put the per-row outcome on its own business object, one instance per processed row, with a SINGLE_INSTANCE field pointing at the target of the business process. For an Equipment Request business process whose target is EquipmentRequest:
{
"id": 2,
"name": "EquipmentRequestResult",
"label": "Equipment Request Result",
"defaultSecurityDomains": ["ManageEquipmentRequests"],
"defaultCollection": {
"name": "equipmentRequestResults",
"label": "All Equipment Request Results"
},
"fields": [
{ "id": 1, "name": "request", "label": "Request", "type": "SINGLE_INSTANCE", "target": "EquipmentRequest" },
{ "id": 2, "name": "lineNumber", "label": "Line Number", "type": "INTEGER" },
{ "id": 3, "name": "status", "label": "Status", "type": "TEXT" },
{ "id": 4, "name": "message", "label": "Message", "type": "TEXT" }
]
}
Extend indexes every SINGLE_INSTANCE field automatically and generates a query parameter for it. That makes GET /equipmentRequestResults?request={requestId} the efficient way to fetch one request's results. It's better than a MULTI_INSTANCE list on the request, which Workday advises keeping below 1,000 instances per field and which can't be indexed.
If the outcome is one value for the whole run, such as an overall status or a total, a field on the target object itself is enough. Workday's Request Credit Card sample does that: its business-process orchestration PATCHes the target request with the values it worked out.
Write the results from the orchestration
Inside the orchestration, get the target's ID from the event. See getting the WID of the business process and its target. Then process the rows and collect one result per row.
Write the results with the business object's bulk API. POST, PUT, PATCH and DELETE accept ?bulk=true with a data array, up to 100 instances per request:
POST /apps/{appReferenceId}/v1/equipmentRequestResults?bulk=true
{
"data": [
{ "request": { "id": "{requestId}" }, "lineNumber": 1, "status": "Created", "message": "Asset 4471 assigned" },
{ "request": { "id": "{requestId}" }, "lineNumber": 2, "status": "Failed", "message": "Cost Center is inactive" }
]
}
For more than 100 rows, put the write inside a Batch Loop with a Size strategy and a Data Breakpoint Number of 100, and send one bulk request per batch.
Read the bulk response. Each entry in data has its own status (201 for created, 400 with an errors array for a refusal), and the responses come back in the same order as the request items. Check each entry's status, not just whether the step ran, and log any entry that isn't 201.
Build the path with context.appReferenceId() rather than a hard-coded app ID, as Workday's samples do, so the same orchestration works in every tenant you deploy to.
Make the next step wait for the results
On the Orchestration Service step, select Advance business process step when orchestration completes. Without it, the business process moves to the next step without waiting for the orchestration, and the reviewer can open the page before any results exist.
If the orchestration fails, the reviewer should see that, not an empty grid that looks like zero problems. (If the step instead sits waiting, see a business process orchestration stuck at In Progress.) Write an overall status on the target (for example processingStatus = Failed) in the orchestration's error handling, and show it at the top of the page.
Whether the service step runs again after a send back depends on how the business process definition routes the event back. If it can run more than once for the same event, make the orchestration replace that request's earlier results (bulk DELETE the old ones first) rather than add to them. That's a design recommendation, not something Workday enforces. The Business Object API supports bulk DELETE. Workday rules it out only in a PMD.
Read them on the next step's page
The page route decides what the page receives:
- An approval step's
pageRoute, such as/approve/{eventId}, gives the pagequeryParams.eventId. - An action step's
pageRoute, such as/actionStep/{eventStepId}, gives itqueryParams.eventStepId. CallGET /eventSteps/{ID}on the Business Process service to get the step'sevent.
From the event, the page reaches the target through the event's for field, then the results through the indexed field. A view page for an approval step:
"endPoints": [
{
"name": "event",
"baseUrlType": "workday-bp",
"url": "<% `events/{{queryParams.eventId}}` %>",
"authType": "sso"
},
{
"name": "results",
"baseUrlType": "app",
"url": "<% 'equipmentRequestResults?request=' + event['for'].id %>",
"authType": "sso"
}
]
Here workday-bp and app are data providers defined in the app's AMD, as in Workday's Request Credit Card sample. Show results.data in a grid with rows bound to <% results.data %>. Workday recommends a view page for approval steps. The framework adds the buttons for the step's allowed actions (Approve, Deny, Send Back) itself, and an edit page on an approval step won't call any outbound endpoint.
The page's endpoints run as the signed-in reviewer (authType sso), so the reviewer needs View access to the result object's security domain. The orchestration's ISU or run-as user needs Modify on the same domain to write the results.
Keep the history honest
Workday doesn't lock business object data during or after a business process. The event details page shows the data as it is now, not as it was at the time of the event, although the business object's audit trail records every change. If reviewers must see the results exactly as they were when they approved, don't let anything edit the result instances after the step completes.
Prove it end to end
Submit a test request with at least one row you expect to fail. Then check:
- The run in Orchestration Activity is SUCCESS, and its log shows a 201 for every saved result row.
GET /equipmentRequestResults?request={requestId}returns one instance per row, with the expected status on the failing one.- Signed in as the approver, the inbox item opens only after the run finishes, and the grid shows the same rows. Open it as the real approver role, not as an administrator. That's the only way to see the security the reviewer actually has.
Sources checked
- Trigger an Orchestration from a Business Process
- Steps: Create Extend App Orchestrations
- Concept: Business Process Steps and Page Routes
- Reference: Design Considerations for Business Processes
- Reference: Business Object API Requests
- Example: Order of Responses in Bulk Operations on Business Objects
- Example: Query Parameters in Business Object API Requests
- Reference: Model Component Attributes
- Reference: Batch Loop
- Event Steps REST APIs