Skip to content

Create a Wire Transfer from Subledger

The API can programmatically create outgoing domestic wire transfers (wire20022), populating the ISO 20022 message with data from the subledger — no need to build a pacs.008 file yourself. The bank composes the message from your stored Subclients and Subclient Accounts and runs it through the same validation as a submitted WIRE20022 file.

Domestic (US) only: the destination Subclient must have a US address and the destination account a 9-digit US routing number (ABA). International wires still require submitting a WIRE20022 file.

The parties of the wire map to your subledger records as follows:

Request field Wire party (ISO 20022) Notes
origin_subclient_id (+ account) Debtor (Dbtr) The sender. The account funds the wire: core or virtual
true_sender_subclient_id Ultimate Debtor (UltmtDbtr) Optional: the true originator when sending on behalf of (POBO)
destination_subclient_id (+ account) Creditor (Cdtr) The beneficiary. The account must be external
ultimate_beneficiary_subclient_id Ultimate Creditor (UltmtCdtr) Optional: the ultimate beneficiary (COBO)

Every party is emitted with a fully structured address taken from the Subclient's stored address: at least a city and a country are required, and US addresses need the 2-letter state code and a 12345 or 12345-6789 ZIP format. Requests referencing a Subclient whose name or address cannot produce a valid wire are rejected with a message explaining what to fix — values are never truncated or rewritten.

Note the difference between two similarly named fields:

  • purpose_code / purpose_code_proprietary — the ISO 20022 purpose of the wire (Purp). One of the two is required; see the purpose codes reference.
  • purpose — free text stored on the subtransaction only (when a true sender or ultimate beneficiary is given). It never reaches the wire.

Prerequisites

The code snippets provided in this scenario require the following variables:

  • BASE_API_URL - The target GIB API domain, which can be found in the API-Reference.
  • API_KEY - The API key setup by the client for using the API. Can be generated using the Dashboard.
  • CORE_ACCOUNT_NUMBER - The account number of the user's core account funding the wire.

Step 1 - Create Subclient To Represent Your Company

Create a Subclient for your company if one does not already exist (this becomes the wire's Debtor). If one exists, query the ID using the Subclient: Get endpoint or the API Dashboard.

url = f"{BASE_API_URL}/subclient"

payload = {
    "name": "Test Fintech",
    "external_key": "ABC-123-0001",
    "type": "business",
    "relationship_type": "root",
    "data": {
        "type": "business",
        "legal_name": "Test Fintech LLC",
    },
    "address": {
        "street_address_1": "123 Somewhere St",
        "city": "Sometown",
        "state_province": "CA",  # 2-letter code for US addresses
        "postal_code": "94105",
        "country_alpha_2_code": "US",
    },
}

headers = {
    "Content-type": "application/json",
    "Authorization": f"{API_KEY}"
}

response = requests.request("POST", url, json=payload, headers=headers)
response.raise_for_status()

ORIGIN_SUBCLIENT_ID = response.json()["id"]

Step 2 - Create The Funding Subclient Account

Attach the core account that funds the wire (a virtual account works the same way - Follow this link for steps to create a Subclient and virtual account).

url = f"{BASE_API_URL}/subclient/{ORIGIN_SUBCLIENT_ID}/account"

payload = {
    "type": "core",
    "account_number": f"{CORE_ACCOUNT_NUMBER}",
    "routing_number": "091208154",  # Primary GIB Routing Number
    "description": "Test Fintech Operating Account",
    "active": True,
}

headers = {
    "Content-type": "application/json",
    "Authorization": f"{API_KEY}"
}

response = requests.request("POST", url, json=payload, headers=headers)
response.raise_for_status()

ORIGIN_SUBCLIENT_ACCOUNT_ID = response.json()["id"]

Step 3 - Create The Beneficiary Subclient And External Account

The beneficiary needs a Subclient with a complete US address and an external account holding the beneficiary bank's routing number and the account number at that bank.

url = f"{BASE_API_URL}/subclient"

payload = {
    "name": "Jane Beneficiary",
    "external_key": "ABC-123-0002",
    "type": "natural_person",
    "relationship_type": "beneficiary",
    "data": {
        "type": "natural_person",
        "first_name": "Jane",
        "last_name": "Beneficiary",
    },
    "address": {
        "street_address_1": "456 Elsewhere Ave",
        "city": "Otherville",
        "state_province": "NY",
        "postal_code": "10001",
        "country_alpha_2_code": "US",
    },
}

headers = {
    "Content-type": "application/json",
    "Authorization": f"{API_KEY}"
}

response = requests.request("POST", url, json=payload, headers=headers)
response.raise_for_status()

DESTINATION_SUBCLIENT_ID = response.json()["id"]
url = f"{BASE_API_URL}/subclient/{DESTINATION_SUBCLIENT_ID}/account"

payload = {
    "type": "external",
    "account_number": "1234567",  # The beneficiary's account at their bank
    "routing_number": "021000021",  # The beneficiary bank's ABA
    "description": "Jane's Checking Account",
    "active": True,
}

headers = {
    "Content-type": "application/json",
    "Authorization": f"{API_KEY}"
}

response = requests.request("POST", url, json=payload, headers=headers)
response.raise_for_status()

DESTINATION_SUBCLIENT_ACCOUNT_ID = response.json()["id"]

Step 4 - Create The Wire Transfer

url = f"{BASE_API_URL}/transaction/from-subledger"

payload = {
    "type": "wire20022",
    "origin_subclient_account_id": ORIGIN_SUBCLIENT_ACCOUNT_ID,
    "destination_subclient_account_id": DESTINATION_SUBCLIENT_ACCOUNT_ID,
    "external_key": "WIRE-2027-0042",  # Enables the duplicate check by external key
    "amount": 100050,  # In cents
    "purpose_code": "GDSV",  # Purchase/sale of goods and services
    "payment_info": "Invoice 42",  # Remittance information: one line, at most 140 characters
    "test": False,
    "simulate": False,
}

headers = {
    "Content-type": "application/json",
    "Authorization": f"{API_KEY}"
}

response = requests.request("POST", url, json=payload, headers=headers)
response.raise_for_status()

Optional request fields:

  • settlement_date - Today (before the cutoff, see below) or a future banking day. A future date creates the transaction in the pending_settlement state until that day.
  • charge_bearer - Who bears the wire's charges (ChrgBr): SLEV (default - charges follow the payment scheme's rules) or SHAR (shared - receiving or intermediary bank charges may be deducted from the transferred amount).
  • reference - Your reference for the wire. It becomes the PmtId/EndToEndId (at most 35 characters, defaults to NOTPROVIDED) and is returned as the transaction's reference.
  • true_sender_subclient_id / ultimate_beneficiary_subclient_id - POBO/COBO parties (see the table above); a subtransaction is created when one of them is given.

When settlement_date is omitted, the bank determines it automatically:

Condition Settlement Date
Before 4:00 PM CT on a banking day Today
After 4:00 PM CT, or on a non-banking day Next banking day

The response contains the created transaction, including the generated trace reference (Fedwire IMAD + UETR - also returned in the X-Generated-Transaction-Trace-References header). The message identifiers, settlement instructions and the bank's own routing details are always set by the bank and cannot be supplied in the request.

Resources

For more information on the endpoints used in this scenario see these API docs:

Related guides: