Manual Account Linking
IMPORTANT!
Manually linking is allowed ONLY for testing ACHNow, RTP, FedNow transactions in Sandbox or with explicit permission from Sila in Production.
This endpoint allows bank accounts can be linked manually rather than through Plaid or MX.
Permission must be given from Sila to allow Production use of this endpoint.
Sandbox ACHNow, RTP, FedNow Testing
A specific routing number is required to successfully transact with RTP and FedNow in Sandbox. So, these accounts will need to be manually linked.
Bank accounts linked normally through Plaid and MX will work as expected for regular ACH transactions and wires in Sandbox.
Testing requirement:
In order to successfully test RTP and FedNow transactions, use the below routing number in this request:
"routing_number": "122105155"
IN PRODUCTION there is no need to manually link bank accounts for successful RTP and FedNow transactions. Link accounts like normal using Plaid or MX.
Linking a Receive Only Bank Account versus Push/Pull Both
Available in Sandbox as of release 0.2.125 - September 2, 2025
Two new features are now available to better support different use cases for manually linking accounts.
These features must be enabled to be used, and Compliance must provide approval first. If neither of these features is enabled, you will not be able to manually link external bank accounts and must use either Plaid or MX.
- Push / Receive Only: manually linked bank accounts can only receive funds. Funds cannot be pulled into a Sila wallet / Priority ledger account from these external bank accounts.
- Push and Pull Both: manually linked bank accounts act by default like accounts linked with Plaid or MX. They can be used to both pull funds into a wallet, or receive funds pushed out of a wallet. You can also link receive only bank accounts using the new parameters detailed below.
Applicable fields in the request body:
ach_credit_enabled
- ability to push funds out of a Sila wallet into a manually linked bank account.ach_debit_enabled
- ability to pull funds into a Sila wallet from a manually linked bank account.
You can call /get_accounts or /get_payment_methods to view the values set for these fields on manually linked accounts.
Receive Only Use Case
If you have been approved to manually link bank accounts meant ONLY to receive funds.
- Both new fields are optional, and will default to:
ach_credit_enabled: true
ach_debit_enabled: false
- Attempting to set
ach_debit_enabled
totrue
will result in a 403 error.
Both Push and Pull Use Case
If you have been approved to manually link accounts that will have the same push/pull ability as an account linked via Plaid or MX.
- Both new fields are optional, and will default to:
ach_credit_enabled: true
ach_debit_enabled: true
- You can set a bank account to be receive only by setting the values as:
ach_credit_enabled: true
ach_debit_enabled: false
Request
POST /0.2/link_account HTTP/1.1
Host: sandbox.silamoney.com
// if using OAuth2
Authorization: Bearer [GENERATED JWT TOKEN HERE]
// if using ECDSA
authsignature: [GENERATED AUTHSIGNATURE HEX STRING HERE]
usersignature: [GENERATED USERSIGNATURE HEX STRING HERE]
Content-Type: application/json
{
"header": {
"created": 1234567890,
"app_handle": "app_handle",
"user_handle":"user_handle",
"version": "0.2",
"reference": "ref"
},
"account_number": "123456789012", // required
"routing_number": "123456789", // required
"account_type": "CHECKING",
"account_name": "Custom Account Name",
"ach_credit_enabled": true/false,
"ach_debit_enabled": true/false
}
***
HTTP/1.1 200 OK
{
"success": true,
"message": "Bank account successfully linked.",
"reference": "ref",
"account_name": "Custom Account Name",
"payment_instrument_id": "<uuid>",
"provider": "manual",
"web_debit_verified": true,
"capabilities": {
"aba_routing_number": "<routing_number>",
"fednow": {
"credit_enabled": false,
"debit_enabled": false
},
"rtp": {
"credit_enabled": true,
"debit_enabled": true
}
},
"status": "SUCCESS",
"sila_reference_id": "auto-generated uuid"
}
// Direct account-linking flow for receive-only entities only
// Restricted by use-case, contact Sila for approval
const res = await Sila.linkAccountDirect(
userHandle,
walletPrivateKey,
accountNumber,
routingNumber,
accountName,
accountType,
); // Account Type and Account Name parameters are not required
//The only permitted account type is "CHECKING"
// Success Response Object
console.log(res.statusCode); // 200
console.log(res.data.reference); // Random reference number
console.log(res.data.status); // SUCCESS
console.log(res.data.success);
console.log(res.data.message); // Bank account successfully linked
console.log(res.data.account_name);
console.log(res.data.match_score);
console.log(res.data.account_owner_name);
console.log(res.data.entity_name);
### Direct account-linking flow for receive-only entities only
### Restricted by use-case, contact Sila for approval
payload={
"user_handle": "user.silamoney.eth", # Required
"account_number": "123456789012", # Required
"routing_number": "123456789", # Required
"account_type": "CHECKING", # Optional (default value is CHECKING)
"account_name": "Custom Account Name" # Optional (default value is "default"),
}
User.linkAccount(silaApp,payload,user_private_key)
### Success Response Object
{
status: 'SUCCESS',
success: True,
message: 'Bank account successfully manually linked.',
reference: 'f9f23fc2-26e7-4358-99c8-75cb8aec76fb',
account_name: 'default',
status_code: 200,
match_score: 0.825,
account_owner_name: 'Sally Smith',
entity_name: 'Sally Smith'
}
### Failure Response Object
{
status: 'FAILURE'
}
//NOTE - as of Java SDK v0.2.21, we have refactored requests to make them more flexible
//for future modifications. Below are examples of the new method and the original method.
//The original method will be deprecated at some point in future SDK versions, but is
//still valid for backwards compatibility purposes. Please use the new method moving
//forward as you upgrade your SDK
// Direct account-linking flow for receive-only entities only
// Restricted by use-case, contact Sila for approval
LinkAccountRequest request = LinkAccountRequest.builder()
.accountName("default")
.accountNumber("123456789012")
.routingNumber("123456789")
.accountType("CHECKING")
.userHandle("user handle")
.userPrivateKey("user private key")
.build();
LinkAccountResponse response = LinkAccount.send(request);
response.getStatus();
response.getAccountName();
response.getMessage();
response.getReference();
//--------------------------------------------
//This is the original method (still valid but will soon be deprecated)
// Direct account-linking flow for receive-only entities only
// Restricted by use-case, contact Sila for approval
String userHandle = 'user.silamoney.eth';
String accountName = 'direct'; // Your desired account name
String accountNumber = '123456789012';
String routingNumber = '123456789';
String accountType = 'CHECKING'; // Currently the only allowed value
String userPrivateKey = 'some private key';
ApiResponse response = api.linkAccount(userHandle, userPrivateKey, accountName, accountNumber, routingNumber, accountType);
// Success Response
System.out.println(response.getStatusCode()); // 200
LinkAccountResponse parsedResponse = (LinkAccountResponse) response.getData();
System.out.println(parsedResponse.getStatus()); // SUCCESS
System.out.println(parsedResponse.isSuccess()); // true
System.out.println(parsedResponse.getReference()); // Reference number
System.out.println(parsedResponse.getMessage()); // Successfully linked
System.out.println(parsedResponse.getAccountName()); // Your desired account name
System.out.println(parsedResponse.getMatchScore()); // Match score
// Direct account-linking flow for receive-only entities only
// Restricted by use-case, contact Sila for approval
$userHandle = 'user.silamoney.eth';
$accountName = 'Custom Account Name'; // Defaults to 'default' if not provided. (not required)
$routingNumber = '123456789'; // The routing number.
$accountNumber = '123456789012'; // The bank account number
$userPrivateKey = 'some private key'; // The private key used to register the specified user
$accountType = 'CHECKING'; // The account type (not required). Only available value is CHECKING
// Call the api
$response = $client->linkAccountDirect($userHandle, $userPrivateKey, $accountNumber, $routingNumber, $accountName, $accountType);
// Success 200
echo $response->getStatusCode(); // 200
echo $response->getData()->getStatus(); // SUCCESS
echo $response->getData()->getReference();
echo $response->getData()->getMessage();
echo $response->getData()->getAccountName();
echo $response->getData()->getAccountOwnerName();
//NOTE - as of C# SDK v0.2.21, we have refactored requests to make them more flexible
//for future modifications. Below are examples of the new method and the original method.
//The original method will be deprecated at some point in future SDK versions, but is
//still valid for backwards compatibility purposes. Please use the new method moving
//forward as you upgrade your SDK
// Direct account-linking flow for receive-only entities only
// Restricted by use-case, contact Sila for approval
LinkAccountRequest request = new LinkAccountRequest{
UserHandle = "user handle",
UserPrivateKey = "user private key",
AccountName = "account name",
AccountNumber = "account number",
RoutingNumber = "routing number",
AccountType = "account type"
};
LinkAccountResponse response = LinkAccount.Send(request);
response.Success;
response.Account;
response.AccountOwnerName;
response.Message;
response.Reference;
response.Status;
response.MatchScore; Account Type and Account Name parameters are not required
//--------------------------------------
//This is the original method (still valid but will soon be deprecated)
// Direct account-linking flow for receive-only entities only
// Restricted by use-case, contact Sila for approval
ApiResponse<object> response = api.LinkAccountDirect(userHandle, walletPrivateKey, accountNumber, routingNumber, accountType, accountName); // Account Type and Account Name parameters are not required
// Success Response Object
Console.WriteLine(response.StatusCode); // 200
Console.WriteLine(((LinkAccountResponse)response.Data).Reference); // Random reference number
Console.WriteLine(((LinkAccountResponse)response.Data).Status); // SUCCESS
Console.WriteLine(((LinkAccountResponse)response.Data).Message); // Bank account successfully linked.
Console.WriteLine(((LinkAccountResponse)response.Data).AccountName); // Account Name.
Console.WriteLine(((LinkAccountResponse)response.Data).MatchScore);
Request Attributes
Key | Type | Description |
---|---|---|
header | JSON object | Required. Required keys: created - Unix epoch timestamp in seconds. Must not be future-dated and must not be dated more than 5 minutes in the past. app_handle - your app handle user_handle - the user_handle this bank account is being linked to. Optional keys: reference: String. Can be any value for your own reference. If not provided, one will be assigned. version: Cannot be null if key is present. Valid values: 0.2, v0.2, V0.2 |
account_number | string | Required. No more than 17 characters. Must be unique per entity. |
routing_number | string | Required. Valid routing number. Use 122105155 to test RTP and FedNow transactions in Sandbox. |
account_type | string | Optional. Valid options: CHECKING or SAVINGS |
account_name | string | Optional, but strongly recommended. |
ach_credit_enabled | boolean | Optional. Sets the ability for funds to be pushed out of a Sila wallet / Priority ledger account into a manually linked bank account . PUSH ONLY default: true PUSH/PULL BOTH default: true Can be set to false - results in a linked bank account that can only be used to pull funds into a Sila wallet. |
ach_debit_enabled | boolean | Optional. Sets the ability for funds to be pulled from manually linked bank accounts into a Sila wallet / Priority ledger account. PUSH ONLY default: false Cannot be set to true - results in a 403 error if attempted.PUSH/PULL BOTH default: true Can be set to false - results in a bank account that can only receive funds. |
Responses
Status Code | success Attribute | Description |
---|---|---|
200 | true | Bank account successfully linked. |
202 | false | Bank account linked, but in a frozen state. Reach out to Support. |
400 | false | Check validation_details for more information. |
401 | false | authsignature or usersignature header was absent or incorrect. |
Updated 7 days ago