Workday Extend enforces two distinct sets of file attachment limits depending on how the file enters Workday: Model Attachment Objects and the Workday Graph API allow up to 30 MB per file, whereas the PMD fileUploader widget caps uploads at 10 MB per file (10,485,760 bytes) and a maximum of 5 files per widget (50 MB total). When you use fileUploader on an Extend page—even if the page submits to a Graph API mutation or an Attachment Object endpoint—the stricter 10 MB fileUploader limit always overrides the 30 MB API limit.

Beyond file size and count caps, most upload and retrieval failures stem from blocked file extensions, missing Collection suffixes on Attachment Object URLs, reversed multipart/form-data part ordering, or missing Accept: application/json headers when reading attachment metadata.

Documented attachment and fileUploader limits

Component or Layer Limit Type Documented Maximum Notes
Component or LayerPMD fileUploader widgetLimit TypeMaximum file sizeDocumented Maximum10 MB (10,485,760 bytes)NotesCan be lowered (never raised above 10 MB) on page load via maxFileSize
Component or LayerPMD fileUploader widgetLimit TypeMaximum files per uploadDocumented Maximum5 files (50 MB total)NotesSet "singular": true to restrict the user to 1 file; minRows cannot exceed 5
Component or LayerPMD fileUploader widgetLimit TypeImage auto-resize (maxImageDimension)Documented Maximum1 to 5,000 pixels (800–2,000 recommended)NotesResizes longest edge for bmp, gif, jpg, jpeg, png sent to Workday REST APIs
Component or LayerModel Attachment ObjectsLimit TypeAttachment definitions per appDocumented Maximum5 per appNotesUp to 5 indexed fields per attachment object (plus auto-indexed SINGLE_INSTANCE fields)
Component or LayerModel Attachment ObjectsLimit TypeMaximum file size (direct API)Documented Maximum30 MBNotesOverridden by the 10 MB fileUploader limit when uploaded from an Extend page
Component or LayerWorkday Graph APILimit TypeMaximum file sizeDocumented Maximum30 MBNotesVirus-scanned on upload; downloadID links expire after 1 hour
Component or LayerTenant instance storageLimit TypeTotal Business + Attachment Object instancesDocumented Maximum10 million (Extend Core/Legacy) or 100 million (Extend Professional / Extend AI)NotesShared across all Extend business objects and attachment objects in the tenant; check Extend Object Instance Count report

Allowed and blocked file types (fileTypes)

By default, if you omit the fileTypes attribute on a fileUploader widget, Workday only permits six extensions:

  • doc, docx, html, htm, pdf, txt

To allow additional formats (such as images or spreadsheets), pass a comma-separated list of extensions ("fileTypes": "pdf,png,jpg,jpeg") or set "fileTypes": "*" to allow all non-blocked types. You can customize the alert shown on invalid extensions with fileTypesErrorMessage.

Hard-blocked extensions and tenant-level overrides

Even when you set "fileTypes": "*", fileUploader permanently blocks executable and script extensions:

  • bas, bat, com, exe, js, lnk, ocx, reg, sct, sys, vb, vbe, vbs, wsc, wsf, wsh

In addition, your tenant's global file-type whitelist governs both fileUploader and the Graph API. If a file extension is disabled in the Edit Tenant Setup - System task (under File Type Setup Instructions), uploads of that file type will fail even when listed in fileTypes.

How Model Attachment Objects work

When you create an Attachment Object in your Extend app model, Workday automatically generates four system fields on every instance:

  • file: Stores the binary file or image as a base64-encoded string.
  • fileName: Writable via POST and PUT requests.
  • fileLength: Read-only integer derived automatically from the uploaded file's byte size.
  • contentType: Read-only MIME descriptor derived automatically from the uploaded file.

Workday also provides the File Size report field (secured by the Public Reporting Items domain in the Tenant Non-Configurable functional area) so you can inspect attachment disk usage in custom reports or WQL queries.

Single-attachment vs. multi-attachment data modeling

  • One attachment per Business Object: Define a SINGLE_INSTANCE field on the Business Object that targets the Attachment Object.
  • Multiple attachments per Business Object: Define a SINGLE_INSTANCE field on the Attachment Object itself that targets the parent Business Object. Each uploaded file creates a separate Attachment Object instance referencing the same parent Business Object record.

Configuring fileUploader outbound bindings

The fileUploader widget only supports POST outbound endpoints. The required valueOutBinding syntax depends on which API receives the upload:

Target Outbound Endpoint valueOutBinding Format Endpoint url & Metadata Rules
Target Outbound EndpointModel Attachment Object REST APIvalueOutBinding Format<endpointName>:<defaultCollectionName>Endpoint url & Metadata RulesAppend Collection to the endpoint url (for example, charityLogosCollection). You must specify metadata on fileUploader (use "<% { : } %>" if there are no custom fields).
Target Outbound EndpointWorkday Graph API mutationvalueOutBinding Format<endpointName>:<partName>Endpoint url & Metadata RulesOmit the numeric index in valueOutBinding (use createAttachments:file), then map myFileUploader.rows to 'file' + index in graphQuery.variables.
Target Outbound EndpointWorkday REST sub-resource (file only)valueOutBinding Format<endpointName>Endpoint url & Metadata RulesFor endpoints like POST /entries/{id}/attachments that take only the binary file, specify just the outbound endpoint name.
Target Outbound EndpointWorkday multipart REST API (file + JSON)valueOutBinding Format<endpointName>:<filePartName>Endpoint url & Metadata RulesBuild self.data.jsonData in the endpoint's onMultiPartSend script and reference "file": "cid:<filePartName>".
Target Outbound EndpointThird-party multipart REST APIvalueOutBinding Format<endpointName>:<filePartName>Endpoint url & Metadata RulesBind JSON fields on other widgets using <endpointName>:<formDataName>:<jsonFieldName> or populate self.data.<formDataName> in onMultiPartSend.

Uploading to a Model Attachment Object in PMD

When uploading to a Model Attachment Object, always target its collection endpoint by appending Collection to the defaultCollection name in the outbound endpoint url, and always include the metadata attribute on fileUploader:

{
  "type": "fileUploader",
  "id": "receiptUploader",
  "label": "Upload Receipts",
  "valueOutBinding": "postReceipts:expenseReceipts",
  "fileTypes": "pdf,png,jpg,jpeg",
  "maxImageDimension": 1200,
  "singular": false,
  "metadata": "<% { 'uploadedBy': {'id': worker.id} } %>"
}

If your attachment metadata depends on values the user types into other widgets on the same page (which change after initial page load), modify self.data.metadata.<defaultCollectionName> inside the outbound endpoint's onMultiPartSend event handler:

{
  "name": "postReceipts",
  "baseUrlType": "app",
  "url": "expenseReceiptsCollection",
  "httpMethod": "POST",
  "authType": "sso",
  "exclude": "<% empty(receiptUploader.children) %>",
  "onMultiPartSend": "<%
    for (var item : self.data.metadata.expenseReceipts) {
      item.fileDescription = descriptionInput.value;
    }
    self.data;
  %>"
}

Uploading and downloading files with the Workday Graph API

To upload files through a Graph API mutation (POST https://api.workday.com/graphql/v1), bind "valueOutBinding": "createAttachments:file" on fileUploader (without a trailing number) and use myFileUploader.rows.map((row, index) => ...) in graphQuery.variables to generate zero-indexed part names (file0, file1) for the File-typed attachmentContent field:

"logos": "<%
  myFileUploader.rows.map((row, index) => {
    {
      'attachmentContent': 'file' + index,
      'uploadedBy': graph:createId(worker.id),
      'uploadedDate': date:getTodaysDate(date:getDateTimeZone(userTimeZone))
    }
  });
%>"

To download files via Graph API, query the attachment object's downloadID, descriptor, and contentType fields. Because Graph API queries cannot return raw binary streams directly, pass the returned downloadID (which is valid for 1 hour) to the Attachments REST API inside your fileUploaderRow's attachmentList:

https://api.workday.com/attachments/v1/graphql/<% file.downloadID %>

Troubleshooting common file upload and download errors

1. HTTP 500 Internal Server Error on direct multipart/form-data REST uploads

When calling a Model Attachment Object REST API directly from an orchestration or external client using multipart/form-data, the order of the form parts is strictly enforced. You must send the JSON data form part (containing "file": "cid:filedata" and your metadata fields) before the binary filedata part. Sending the binary file part before the data JSON part causes a 500 Internal Server Error.

2. Uploaded files silently fail to save when using onMultiPartSend

When fileUploader constructs a request for a Model Attachment Object collection endpoint, it populates each array element in self.data.metadata.<defaultCollectionName> with a "file": "cid:<defaultCollectionName><index>" reference. If your onMultiPartSend script overwrites the array elements instead of mutating individual properties on each element—or if console.info(...) is the last line of the script instead of self.data;—the cid: file references are lost and Workday does not save the files.

Also, never use valueOutBinding on other page widgets (such as hidden or text widgets) to write to the same Attachment Object endpoint when metadata is configured on fileUploader.

3. GET on an Attachment Object returns raw binary instead of JSON metadata

Calling GET /apps/{appId}/v1/{attachmentCollection}/{id} without headers returns the raw binary file content. If you want to read the Attachment Object's metadata fields (fileName, fileLength, contentType, and custom fields), you must include the HTTP header Accept: application/json.

4. Downloaded Extend attachment has the wrong filename or fails to open

When configuring an attachmentList inside a fileUploaderRow to download a Model Attachment Object via REST, do not bind attachmentName to the object's descriptor. Always bind attachmentName to the attachment object's fileName field (for example, "attachmentName": "<% getData.image.fileName %>"). (By contrast, when querying an attachment via the Graph API, the Graph schema returns the filename inside descriptor.)

5. Bulk delete (?bulk=true) fails on Attachment Objects

While Custom Business Objects support ?bulk=true on POST, PUT, PATCH, and DELETE, the DELETE operation on Model Attachment Objects does not support bulk=true. Delete attachment instances individually by ID using deleteEndPoint.

How kiweely fits

kiweely configures your Model Attachment Objects, generates the matching fileUploader and attachmentList PMD bindings, and verifies multipart upload and download flows in your real tenant. In your Workday personal developer account and its tenant, Kiweely Personal is free (Kiweely Personal is free for your Workday personal developer account and its tenant. Any other tenant needs a Personal licence, $99 a month, or a seat in your firm.).

Frequently asked questions

Can I increase the fileUploader maximum file size above 10 MB?

No. Although Model Attachment Objects and the Workday Graph API support files up to 30 MB when called directly via API, the PMD fileUploader widget enforces a hard ceiling of 10 MB (10,485,760 bytes) per file and 5 files per widget. The maxFileSize attribute on fileUploader can only restrict the size to a smaller byte count between 1 and 10485760.

Why does my fileUploader fail when submitting to an empty Attachment Object with no custom fields?

When binding fileUploader to a Model Attachment Object collection endpoint (<endpoint>:<collection>), the metadata attribute on fileUploader is required even if your Attachment Object has zero custom metadata fields. Set "metadata": "<% { : } %>" to pass an empty map so fileUploader generates the required cid: file references.

How long is a Graph API attachment downloadID valid?

Every downloadID returned by a Workday Graph API query expires after 1 hour. Fetch the downloadID on page load and pass it immediately to https://api.workday.com/attachments/v1/graphql/{downloadID} in your attachmentList widget rather than storing downloadID values in long-lived records.

Sources checked