When you send an approval email using the Submit with Attachment feature, the attached document is sent to all the approvers with the ability to only approve or reject a request. Approvers cannot modify the attachment. Even if the approver modifies the attachment and sends it to the next approver, the original attachment is forwarded to the next approver and not the modified one. It may also happen that all approvers may not agree on the same document by the end of the approval cycle unless approvals are submitted multiple times. 

With the Summer '21 release, you can send modified attachments using the Inbound Attachment Callback. Inbound Attachment Callback allows you to write a callback class to handle attachments on an inbound approval email response. Based on the presence or absence and/or the type of attachment, custom logic can be written to take various actions. This callback is called during the processing of Approval Reply Handler.

To override the Default Inbound Attachment Callback class

  1. Go to Setup > Develop > Custom Settings and click Manage for Approvals Custom Classes.
  2. Click Edit for Custom Classes. 

    If there is no existing entry, you must create one. Click New and in Name, type Custom Classes.

  3. In Inbound Attachment Callback Class, type the name of your callback class. For example, to use the below-mentioned sample code, enter InboundAttachmentHandlerSample as the callback class name. You must provide your own custom logic inside this handler to process attachments and only use this sample code as a reference.

    If you want to use the default class which just replaces the attachment with the ones included in the email response you don’t need to write a custom implementation at all and go to Custom Settings > Approval System Properties and in Inbound Attachment Callback property, enter “Apttus_Approval.InboundAttachmentHandlerTest”.
  4. Click Save.

You must define your class as global for the callback mechanism to work. 

Sample Code: To be used as a reference

/**
 *	Conga Approvals
 *  InboundAttachmentHandlerSample
 *   
 *  @2017-2021 Conga Inc. All rights reserved.
 */
 public with sharing class InboundAttachmentHandlerSample
 	implements CustomClass.IInboundAttachmentCallback {

	// handle attachment
	public static Boolean handleActionAttachment(ID ctxObjectId, String approvalAction, Messaging.InboundEmail email) {
		
		// first verify that there are attachments...
		if (((email.binaryAttachments == null) || email.binaryAttachments.isEmpty())
				&& ((email.textAttachments == null) || email.textAttachments.isEmpty())) {
			return true;	
		}
		
		// associate attachment to context object     
		Attachment ctxAttachment = null; 
		try {
			// binary or text?
			if ((email.binaryAttachments != null) && !email.binaryAttachments.isEmpty()) {
				     
		        // code below overwrites existing attachment (this is the default behavior)
				//ctxAttachment = new Attachment(ParentId = ctxObjectId
		                                      //, Name = email.binaryAttachments[0].filename 
		                                      //, Body = email.binaryAttachments[0].body);
		        //insert ctxAttachment;

				// code below saves a new attachment with day of year appended
				Date dtNow = Date.today();
				List<String> attachmentNameInParts = email.binaryAttachments[0].filename.split('\\.(?=[^\\.]+$)');
				String attachmentName = attachmentNameInParts[0] + '-' + dtNow.dayOfYear() + '.' + attachmentNameInParts[1];
				ctxAttachment = new Attachment(ParentId = ctxObjectId
		                                      , Name = attachmentName 
		                                      , Body = email.binaryAttachments[0].body);
		        insert ctxAttachment;
		        
			} else if ((email.textAttachments != null) && !email.textAttachments.isEmpty()) {
				
		        // code below overwrites existing attachment (this is the default behavior)
				//ctxAttachment = new Attachment(ParentId = ctxObjectId
		                                      //, Name = email.textAttachments[0].filename 
		                                      //, Body = Blob.valueOf(email.textAttachments[0].body));
		        //insert ctxAttachment;
		        
				// code below saves a new attachment with day of year appended
				Date dtNow = Date.today();
				List<String> attachmentNameInParts = email.binaryAttachments[0].filename.split('\\.(?=[^\\.]+$)');
				String attachmentName = attachmentNameInParts[0] + '-' + dtNow.dayOfYear() + '.' + attachmentNameInParts[1];
				ctxAttachment = new Attachment(ParentId = ctxObjectId
		                                      , Name = attachmentName
		                                      , Body = Blob.valueOf(email.textAttachments[0].body));
		        insert ctxAttachment;

			}

			// optionally perform custom logic here

		} catch (Exception e) {
			System.debug('Unable to create an attachment object from provided email attachment.');
			System.debug('Message... ' + e.getMessage());
			System.debug('Type... ' + e.getTypeName());
			System.debug('Stacktrace... ' + e.getStackTraceString());

			return false;
		}
		
		// replace approval process attachment with this new attachment
		try {
			ApprovalsWebService.replaceApprovalEmailAttachment(ctxObjectId, ctxAttachment.Id);
		
		} catch (Exception ex) {
			System.debug('Unable to replace approval process attachment with ' + ctxAttachment.Name);
			System.debug('Message... ' + ex.getMessage());
			System.debug('Type... ' + ex.getTypeName());
			System.debug('Stacktrace... ' + ex.getStackTraceString());

			return false;
		}

		return true;
	}
	 
}
CODE