Remote Deposit Adapter Extension Tutorial
The RemoteDeposit
adapter type is used for the RemoteDeposit workflow inside of digital banking.
Remote Deposit Capture (RDC) is a service that allows individuals and businesses to deposit checks
into their bank accounts without needing to visit a bank branch or ATM.
By default, Remote Deposit is not enabled in a sandbox stack. You can
enable this with the q2 sandbox rdc
command:
$ q2 sandbox rdc
Which operation?
1) Enable
2) Disable
Please make a selection and press Return: 1
Next, we create an Adapter extension just as any other:
Create an extension using
q2 create_extension ExampleRDC
command:$ q2 create_extension New Extension Name: ConvertToDollars What type of extension are you creating? 1) Online (default) 2) SSO (Third Party Integration) 3) Ardent (API) 4) Q2Console (Backoffice) 5) Central (Legacy Backoffice) 6) Adapter <-------- 7) Audit Action 8) Custom Health Check 9) Message Bus 10) Caliper API Custom Endpoint 11) Base Extension Please make a selection and press Return [1]: 6 Select adapter type to generate 1) Account Details 2) Authentication Token 3) Check Image 4) Domestic Wire 5) Deposit Item Image 6) FX Rate 7) Instant Payments 8) International Wire 9) Remote Deposit <-------- 10) Statement Image Please make a selection and press Return: 2
Run
q2 install ExampleRDC
to register the extension.
What did we get? Our extension.py
has the usual boilerplate for a request handler, and a handler class definition with three
methods:
async def get_rdc_transaction_list(self, remote_deposit_capture_request: RemoteDepositRequest) -> list[RDCTransactionResponse]:
async def get_rdc_transaction_detail(self, transaction_detail_request: RDCTransactionHistoryDetailRequest) -> RDCTransactionHistoryDetailResponse:
async def capture_rdc(self, remote_deposit_capture_request: RDCCaptureRequest) -> RDCSubmitResponse:
async def get_validation(self, remote_deposit_validation_request: RDCValidateRequest) -> RDCValidation:
That’s it? From the perspective of an extension developer, yes. By the time this code has executed, the Caliper SDK has:
Intercepted the request from Online
Checked the database for its configured destination
Redirected the request to this Caliper SDK extension
Translated the XML payload containing the request into an easily used object
RemoteDepositRequest()
Remote Deposit Capture is a mobile only workflow for Q2. This workflow is a native iOS and Android workflow and is not available on the web. For development purposes, we have a basic web UI to test the workflow. It is possible for you to build your own Remote Deposit Capture workflow on the web and have it call the adapter you build here. More on that later.
We will build an adapter for the standard workflow. To test this workflow, you will need to download and configure the mobile app on a device. Since iOS simulator doesnt have a camera access, iOS testing needs to be done on a physical device. Android you are able to use the camera in the simulator, so testing this work flow is possible in the Android simulator.
You can download the mobile app to your device following the instructions here:
Once you login, click the Transactions
menu and then Deposit Check
. You will be able to fully validate
the workflow from here.
Let’s take each API one at a time.
Validate RDC
The first call the adapter will receive is the Transaction List call. This call is expected to give back history but since
we are starting fresh, there is no history. get_rdc_transaction_list
should just return an empty list for now.
The second call will be to the get_validations
function. This function is the opportunity to return a dictionary of validation
parameters that will be used by the client to validate the inputs before they are submitted to the server:
{
"DailyAmtRemain": null,
"DailyCountRemain": null,
"WeeklyAmtRemain": null,
"WeeklyCountRemain": null,
"MonthlyAmtRemain": null,
"MonthlyCountRemain": null,
"DailyAmt": null,
"DailyCount": null,
"WeeklyAmt": null,
"WeeklyCount": null,
"MonthlyAmt": null,
"ItemAmt": 13,
"MonthlyCount": null,
"CalDailyAmt": 10001.00,
"CalDailyAmtRemain": 10001.00,
"CalDailyTransaction": 5,
"CalDailyTransactionRemain": 5,
}
There is room for 3 error messages; Amount Limit, Count Limit and Item Limit. The way the native Q2 UI handles multiple messages is that it will show the messages in this priority (applies to Amount and Count):
Calendar Daily Remaining
Daily Remaining
Weekly Remaining
Monthly Remaining
Calendar Daily
Daily
Weekly
Monthly
The get_validations
function is expectd to return a RDCValidation()
object. Simply
instantiate an instance of this class, set the validation properties you want and return it.
async def get_validations(
self, remote_deposit_capture_request: RemoteDepositRequest
) -> RDCValidation:
validations = RDCValidation()
validations.weekly_amount = 7.00
validations.weekly_count = 8
validations.item_amount = 13
return validations
Would produce a UI like this:
Warning
These values must be the appropriate types for the parameters (integers and decimals)

The other optional functionality in this get_validations
is to mark certain accounts as invalid for remote deposit capture.
Maybe you want to remove certain accounts. You can do this by modifying the account list that is passed in and setting the is_valid
property on the remote_deposit_capture_request.accounts
objects:
remote_deposit_capture_request.accounts[0].is_valid = False
Capture RDC
Now its time to capture our first RDC transaction. The UI will send the collected values to the capture_rdc
function. Your
capture_rdc
function is passed a RemoteDepositRequest()
object. This object has
accounts
, selected_account
, amount
, back_image
, front_image
and (optionally, depending on UI config)
check_number
.
The capture_rdc
function needs to return a RDCCaptureResponse()
object. This
object has host_transaction_id
, end_user_messsage
and an optional check_number
. The check_number
can be
set if the RDC system parsed one out of the submitted image, if not, check_number
can be left unset.
async def capture_rdc(
self, remote_deposit_capture_request: RDCCaptureRequest
) -> RDCCaptureResponse: # pragma: no cover
return RDCCaptureResponse(host_transaction_id="123456")

Note
end_user_message
is only used in the case of an error, success shows a generic message. Also, error modals do not show
on the desktop test workflow, only in the actual mobile app.
async def capture_rdc(
self, remote_deposit_capture_request: RDCCaptureRequest
) -> RDCCaptureResponse: # pragma: no cover
return RDCCaptureResponse(host_transaction_id="123456", success=False, end_user_message="Sorry! that didnt work")
Transaction List
Now that we have a successful transaction in the database (Q2_RemoteDepositHistory
table if you are interested in looking)
we can work on the Transaction APIs.
The first API that is called is the get_rdc_transaction_list
. This API is responsible for decodating the transactions in
the Q2 database with the latest status.
If the RDC system only supports history for a certain period of time, that is fine, only return back the list of transactions that the RDC system still has records for. Other history items in the Q2 DB will be ignored.
The key to the returned transaction list is that the host_transaction_id
must match what was returned at capture time. You
are also responsible for setting the approproate RemoteDepositStatus()
. This can be
one of Accepted
, Submitted
, or Rejected
.
async def get_rdc_transaction_list(
self, transaction_list_request: RemoteDepositRequest
) -> list[RDCTransactionResponse]:
"""Using the information provided in `transaction_list_request` param, return a list of Transaction objects"""
self.logger.info(transaction_list_request)
tran_1 = RDCTransactionResponse(
"123456", "description1", "2024-08-26T18:14:32.717", "100.00",
RemoteDepositStatus.Accepted,
"x7954-12"
)
return [tran_1]
Transaction Details
Note
Q2’s mobile app history screen does not utilize the Transaction Details API.
Q2’s UI shows RDC history under Transactions -> Activity Center -> Deposited Checks.


With the transaction list populated with history, the next api to complete is get_rdc_transaction_detail
. This API will
be called when the end user clicks a transaction in the history list. This API is expected to provide back the details (including
the front and back images). The images are not stored in the Q2 DB so the images will be retrieved as needed from the API. We
provide a MockImage()
object for you to try and see what image data looks like.
async def get_rdc_transaction_detail(
self, transaction_detail_request: RDCTransactionHistoryDetailRequest
) -> RDCTransactionHistoryDetailResponse:
self.logger.info(transaction_detail_request)
return RDCTransactionHistoryDetailResponse(
transaction_id=1,
transaction_date="2024-06-17T09:38:29.267-05:00",
transaction_amount="100.00",
transaction_description="YES!",
transaction_status=RemoteDepositStatus.Accepted,
host_ref_number="416947816",
account_number="x7954-12",
front_image=RDCImage(MockImage.get_front(), ImageType.PNG),
back_image=RDCImage(MockImage.get_back(), ImageType.PNG)
)