Power Pages One Step Submit: Uploading Files and Binding Attachments
- Sofia Ng
- Aug 29
- 2 min read
We’ve now got a parent record being created reliably with tokens, payloads, and even lookup fields. That was the backbone. Now it’s time to add the other half - uploading files and linking them to the record in one go.

The flow
Here’s what happens once the parent record is created:
Create a child row in the orderattachments table.
Upload the file into its File column (filecontent).
Bind the attachment back to the parent record through the relationship.
Do that for each file in the picker, and you’ve got a clean, single-step user experience.
Creating the child row
We start by creating an empty row that will hold the file.
async function createChildRow(filename){
const r = await apiJson(`/_api/orderattachments`, "POST", { "name": filename || "file" });
const id = (r.headers.get("entityid")||"").replace(/[{}]/g,"");
if (!id) throw new Error("No entityid returned for attachment");
return id;
}Uploading the file
Next, we upload the binary data to the File column.
async function uploadFile(childId, file){
const r = await apiFetch(`/_api/orderattachments(${childId})/filecontent`, {
method: "PATCH",
headers: {
"Content-Type": "application/octet-stream",
"If-None-Match": "null",
"x-ms-file-name": file.name
},
body: file
});
if (!r.ok) throw await r.json().catch(() => ({ error:{message:r.statusText, status:r.status} }));
}Linking to the parent
Finally, connect the child back to the parent record.
async function bindChildToParent(childId, parentId){
const base = window.location.origin + "/_api";
const url = `/_api/orders(${parentId})/orders_orderattachments/$ref`;
const body = { "@odata.id": `${base}/orderattachments(${childId})` };
const r = await apiFetch(url, {
method:"POST",
headers:{ "Content-Type": "application/json" },
body: JSON.stringify(body)
});
if (!r.ok) throw new Error("Link failed");
}Running uploads in parallel
If a user selects several files, we don’t want them stuck waiting for one file at a time. A simple queue with a parallel limit keeps things quick but controlled:
async function runQueue(tasks, limit=3){
let i=0;
const workers = Array(Math.min(limit, tasks.length)).fill(0).map(async ()=>{
while(i<tasks.length){ const idx=i++; await tasks[idx](); }
});
await Promise.all(workers);
}User feedback
To keep users informed:
Show a progress overlay with a spinner and a counter.
Update the message as each file completes.
Close the overlay once all uploads finish.
This avoids the “click and hope” problem of the default form.
Production guardrails
Before rolling this out widely:
Add an allow-list of file types and size caps.
Handle duplicate names gracefully.
Decide whether to fail all files if one fails, or continue and report the error.
Make sure your Web Role has the right permissions (Create, Append, Append To, Write on File).
What we’ve built
With all three posts combined you now have:
A custom Submit button that creates the record and uploads files in one click.
Resilient token handling and lookups.
Parallel uploads with progress feedback.
Guardrails to keep the system safe and user friendly.
No more two-step process. Just one smooth action.
Looking to streamline your own Power Pages forms? If you’d like help implementing this kind of one-step process in your environment, get in touch - we’d be happy to chat.


Comments