Skip to main content
Participating Frequently
June 18, 2026
3. Existing Functionality/Native Skill

Add Support For Multiple Files In Agent Studio File Slots

Related products:Agent Studio
  • June 18, 2026
  • 16 replies
  • 162 views

This blog post https://www.moveworks.com/us/en/resources/blog/file-slots-file-uploads#one-file-per-slot-no-overwriting states: 

“Right now, you can only upload one file per File Slot. And once it’s uploaded, the user can’t replace it — they’d have to start over.

This is a limitation we built in, not something forced on us. We could support multiple files, we just haven’t yet.”


I’m currently building out employee submitted expense reports in agent studio and would love if they could just bulk upload all their receipts at once rather than 1 at a time. Multiple file support in a single slot would also significantly simplify my conversational workflow design since I wouldn’t need to create a slot for receipt 2, receipt 3, receipt 4, etc.



In fact, in the same blog post, expense reports are mentioned: “Think about what you can build: expense reports with receipts attached”


This capability would be great. Thanks!

    16 replies

    cfinelliAuthor
    Participating Frequently
    July 20, 2026

    @cfinelli Please check our new enhancement that will allow files. 

     

    This is great, thanks Kevin! I just tried submitting a PDF for an expense report and got:


    The PDF receipt could not be processed for this expense submission.

    Please upload your receipt again as an image file (PNG or JPG) using the Upload files button. PDFs are accepted in some cases, but this one could not be read. Once you upload the image, I’ll continue the Coupa submission.

     

     

    After further review of the doc I saw it says “For image and file inputs, we recommend using gpt-5 or newer for the best results.”

     

    On my input mapper for the image extraction (mw.generate_structured_value_action), line 2 is currently set to:
    model: '"gpt-4o"'

     

    Assuming I need to adjust this to gpt-5o, right? Would adjusting the model require me to make any other changes to mapping/syntax in subsequent steps or will it just magically start supporting PDFs after that adjustment? Let me know if I’m missing something here. 

     

    Again, thanks so much for your help & effort here Kevin! 

    Kevin Mok
    Community Manager
    July 21, 2026

    Hey ​@cfinelli - yes please use adjust to the other model if needed, but we are just recommending it. I believe 4o should work too.

    One thing to note for files we introduced a new field which is called `file` so you will need to use that field instead of `image` for any file that comes in

    cfinelliAuthor
    Participating Frequently
    July 21, 2026

    Hey ​@cfinelli - yes please use adjust to the other model if needed, but we are just recommending it. I believe 4o should work too.

    One thing to note for files we introduced a new field which is called `file` so you will need to use that field instead of `image` for any file that comes in

    Got it, so I can just swap out the image field for the file field. So my Input Mapper goes from:
     

    image: data.receipt_file[0]
    model: '"gpt-4o"'
    output_schema: >-
    {
    "type": "object",
    "properties": {
    "expense_amount": {"type": "number"},
    "currency_code": {"type": "string"},
    "expense_date": {"type": "string"},
    "merchant_name": {"type": "string"},
    "expense_description": {"type": "string"},
    "expense_category_name": {"type": "string"},
    "hotel_arrival_date": {"type": ["string", "null"]},
    "hotel_departure_date": {"type": ["string", "null"]},
    "hotel_location_city": {"type": ["string", "null"]}
    },
    "required": ["expense_amount", "currency_code", "expense_date", "merchant_name", "expense_description", "expense_category_name", "hotel_arrival_date", "hotel_departure_date", "hotel_location_city"],
    "additionalProperties": false
    }
    payload: "{}"
    strict: "true"
    system_prompt: '"Extract expense details from this receipt image for a Coupa expense submission. Return the final total
    amount (including tax and tip), the 3-letter ISO 4217 currency code (USD, EUR, GBP, etc.), the transaction date in ISO
    8601 format with timezone (e.g., 2026-05-06T00:00:00-04:00), the merchant or vendor name, a brief 1-sentence
    description for approvers, and the most likely Coupa expense category from this list: Conferences & Seminars, Training
    & Development, Customer Meals & Entertainment (USD), Meals for Traveling Employees, Team Events & Outings, Team Meals,
    Employee Gifts, Office Equipment, Office Supplies, SWAG, Shipping & Postage, Workspace Rental, Marketing Demand
    Capture, Marketing Demand Generation, Marketing Special Events, Marketing Support & Services, Marketing Thought
    Leadership, Car Rental, Ground Transportation (Bus), Ground Transportation (Car - Uber, Lyft, Taxi), Ground
    Transportation (Subway/Train), Mileage - US Employee, Parking & Tolls, Airfare, Hotel & Lodging, International Cell
    Phone Service, Rail Transportation, Employee Personal Cellphone Expense, Internet Service, Donations, Dues and
    Membership Fees, Hosting & Platform Costs, Recruiting, SaaS Subscription Fees. Return the EXACT category name. For
    restaurant or cafe receipts, default to Team Meals unless context indicates customer entertainment or travel. If this
    is a hotel or lodging receipt, also return hotel_arrival_date (check-in) and hotel_departure_date (check-out) in that
    same ISO 8601 format with timezone, plus hotel_location_city (the city where the hotel is located); for any non-hotel
    receipt, return null for all three hotel fields."'

     

    to:


     

    file: data.receipt_file[0]
    model: '"gpt-4o"'
    output_schema: >-
    {
    "type": "object",
    "properties": {
    "expense_amount": {"type": "number"},
    "currency_code": {"type": "string"},
    "expense_date": {"type": "string"},
    "merchant_name": {"type": "string"},
    "expense_description": {"type": "string"},
    "expense_category_name": {"type": "string"},
    "hotel_arrival_date": {"type": ["string", "null"]},
    "hotel_departure_date": {"type": ["string", "null"]},
    "hotel_location_city": {"type": ["string", "null"]}
    },
    "required": ["expense_amount", "currency_code", "expense_date", "merchant_name", "expense_description", "expense_category_name", "hotel_arrival_date", "hotel_departure_date", "hotel_location_city"],
    "additionalProperties": false
    }
    payload: "{}"
    strict: "true"
    system_prompt: '"Extract expense details from this receipt image for a Coupa expense submission. Return the final total
    amount (including tax and tip), the 3-letter ISO 4217 currency code (USD, EUR, GBP, etc.), the transaction date in ISO
    8601 format with timezone (e.g., 2026-05-06T00:00:00-04:00), the merchant or vendor name, a brief 1-sentence
    description for approvers, and the most likely Coupa expense category from this list: Conferences & Seminars, Training
    & Development, Customer Meals & Entertainment (USD), Meals for Traveling Employees, Team Events & Outings, Team Meals,
    Employee Gifts, Office Equipment, Office Supplies, SWAG, Shipping & Postage, Workspace Rental, Marketing Demand
    Capture, Marketing Demand Generation, Marketing Special Events, Marketing Support & Services, Marketing Thought
    Leadership, Car Rental, Ground Transportation (Bus), Ground Transportation (Car - Uber, Lyft, Taxi), Ground
    Transportation (Subway/Train), Mileage - US Employee, Parking & Tolls, Airfare, Hotel & Lodging, International Cell
    Phone Service, Rail Transportation, Employee Personal Cellphone Expense, Internet Service, Donations, Dues and
    Membership Fees, Hosting & Platform Costs, Recruiting, SaaS Subscription Fees. Return the EXACT category name. For
    restaurant or cafe receipts, default to Team Meals unless context indicates customer entertainment or travel. If this
    is a hotel or lodging receipt, also return hotel_arrival_date (check-in) and hotel_departure_date (check-out) in that
    same ISO 8601 format with timezone, plus hotel_location_city (the city where the hotel is located); for any non-hotel
    receipt, return null for all three hotel fields."'

    Is that correct? If so then I think I’m all set, just want to confirm before making changes as this is currently active & live for all employees. Thanks!

    Kevin Mok
    Community Manager
    July 21, 2026

    Yeah that would do it, I will mention that if you need to support both images and files - you may need to work with some conditionals and something similar to:

    ...
    CONDITIONAL():
    condition: data.receipt_file.file_name.$ENDS_WITH(".png") OR data.receipt_file.file_name.$ENDS_WITH(".jpg") OR data.receipt_file.file_name.$ENDS_WITH(".jpeg") OR
    on_pass:
    image: data.receipt_file[0]
    on_fail:
    file: data.receipt_file[0]
    ...

     

    Community Manager
    July 23, 2026

    @cfinelli 

    I’ve added some additional guidance to the documentation. If you collect multiple files in a slot and they’re of mixed type image and file you can do something like the following.

    Replace attachments with whatever the List[file] slot is named

    This will filter out any non image type files in the file argument and filter any image type files out of the file argument


    image: data.attachments.$FILTER((item) => item.file_name.$MATCH('\.(png|jpg|jpeg|gif|webp)$', true).$LENGTH() > 0)

    file: data.attachments.$FILTER((item) => item.file_name.$MATCH('\.(png|jpg|jpeg|gif|webp)$', true).$LENGTH() == 0)

    I would also recommend a more powerful model like gpt-5.4

    cfinelliAuthor
    Participating Frequently
    August 3, 2026

    Thanks both, much appreciated. I was able to resolve this with:
     

    file: IF data.receipt_file[0].file_name.$LOWERCASE().$ENDS_WITH(".pdf") THEN data.receipt_file[0] ELSE NULL
    image: IF data.receipt_file[0].file_name.$LOWERCASE().$ENDS_WITH(".pdf") THEN NULL ELSE data.receipt_file[0]


    However I’m experiencing a separate issue where my expense_amount is returning 0 for all receipts. I’m wondering if there’s another correction neccesary for my input mapping?

     

    file: IF data.receipt_file[0].file_name.$LOWERCASE().$ENDS_WITH(".pdf") THEN data.receipt_file[0] ELSE NULL
    image: IF data.receipt_file[0].file_name.$LOWERCASE().$ENDS_WITH(".pdf") THEN NULL ELSE data.receipt_file[0]
    model: '"gpt-5.4"'
    output_schema: >-
    {
    "type": "object",
    "properties": {
    "expense_amount": {"type": ["number", "null"]},
    "currency_code": {"type": "string"},
    "expense_date": {"type": "string"},
    "merchant_name": {"type": "string"},
    "expense_description": {"type": "string"},
    "expense_category_name": {"type": "string"},
    "hotel_arrival_date": {"type": ["string", "null"]},
    "hotel_departure_date": {"type": ["string", "null"]},
    "hotel_location_city": {"type": ["string", "null"]}
    },
    "required": ["expense_amount", "currency_code", "expense_date", "merchant_name", "expense_description", "expense_category_name", "hotel_arrival_date", "hotel_departure_date", "hotel_location_city"],
    "additionalProperties": false
    }
    payload: data.receipt_file[0]
    strict: "true"
    system_prompt: >-
    "Extract expense details from this receipt image for a Coupa expense submission.

    AMOUNT EXTRACTION (read carefully): Return expense_amount as the single grand total the customer actually paid for
    this transaction. This is the final amount due — the value on the line explicitly labeled Total, Amount Due, Balance
    Due, Grand Total, or Total a Pagar, after tax and tip are included. Identify it by its label and meaning, NOT by which
    number is largest — the correct total is sometimes smaller than other numbers on the receipt. Do NOT return a tax rate
    or VAT/IVA percentage (e.g. values like 13, 21, or 23 shown in a tax-breakdown table are RATES, not amounts), a
    per-item unit price, a line-item subtotal, a tax amount, or a base/net figure from a tax table. When a receipt shows a
    tax breakdown with multiple rows, ignore those rows entirely and take only the labeled final total. If you genuinely
    cannot identify the labeled total with confidence, return null for expense_amount rather than guessing or defaulting
    to zero.

    Also return: the 3-letter ISO 4217 currency code (USD, EUR, GBP, etc.), the transaction date in ISO 8601 format with
    timezone (e.g., 2026-05-06T00:00:00-04:00), the merchant or vendor name, a brief 1-sentence description for approvers,
    and the most likely Coupa expense category from this list: Conferences & Seminars, Training & Development, Customer
    Meals & Entertainment (USD), Meals for Traveling Employees, Team Events & Outings, Team Meals, Employee Gifts, Office
    Equipment, Office Supplies, SWAG, Shipping & Postage, Workspace Rental, Marketing Demand Capture, Marketing Demand
    Generation, Marketing Special Events, Marketing Support & Services, Marketing Thought Leadership, Car Rental, Ground
    Transportation (Bus), Ground Transportation (Car - Uber, Lyft, Taxi), Ground Transportation (Subway/Train), Mileage -
    US Employee, Parking & Tolls, Airfare, Hotel & Lodging, International Cell Phone Service, Rail Transportation,
    Employee Personal Cellphone Expense, Internet Service, Donations, Dues and Membership Fees, Hosting & Platform Costs,
    Recruiting, SaaS Subscription Fees. Return the EXACT category name. For restaurant or cafe receipts, default to Team
    Meals unless context indicates customer entertainment or travel. If this is a hotel or lodging receipt, also return
    hotel_arrival_date (check-in) and hotel_departure_date (check-out) in that same ISO 8601 format with timezone, plus
    hotel_location_city (the city where the hotel is located); for any non-hotel receipt, return null for all three hotel
    fields."

    All of my logs for mw.generate_structured_value_action are returning 0:

     

    "root":{
    "action_name":"generate_structured_value_action"
    "created_at":"2026-08-03T20:03:37Z"
    "error_message":""
    "input_args":{
    "output_schema":{
    "additionalProperties":false
    "type":"o*****t"
    "properties":{
    "hotel_arrival_date":{...}
    "hotel_departure_date":{...}
    "expense_amount":{...}
    "expense_description":{...}
    "currency_code":{...}
    "hotel_location_city":{...}
    "merchant_name":{...}
    "expense_date":{...}
    "expense_category_name":{...}
    }
    "required":[...]
    }
    "file":NULL
    "image":{
    "file_name":"g*****g"
    "location":"u*****g"
    }
    "system_prompt":"E*****."
    "model":"g*****4"
    "strict":false
    "payload":{
    "file_name":"g*****g"
    "location":"u*****g"
    }
    }
    "response":{
    "generated_output":{
    "hotel_arrival_date":NULL
    "hotel_departure_date":NULL
    "expense_amount":0
    "expense_description":"F*****."
    "currency_code":"U*****D"
    "hotel_location_city":NULL
    "merchant_name":"S*****l"
    "expense_date":"2*****0"
    "expense_category_name":"G*****)"
    }
    "openai_chat_completions_response":{
    "choices":[
    0:{
    "message":{
    "content":"{"currency_code": "U*****D", "expense_amount": 0.0, "expense_category_name": "G*****)", "expense_date": "2*****0", "expense_description": "F*****.", "hotel_arrival_date": null, "hotel_departure_date": null, "hotel_location_city": null, "merchant_name": "S*****l"}"
    }
    }
    ]
    }
    }
    }

    Also I noticed that even though I set Strict to “true”, it’s still false in all my logs, which might be the cause of this. What am I doing wrong? Thank you!