Transaction Scenarios

This page shows how to map the counterparty for four common transaction types. Each example assumes that you already created the Entity and Account in the request. For more information, see Creating Entities and Accounts.

If you're migrating from the legacy counterparty schema, expand Legacy payload under each scenario to compare it with the typed version.

The following table summarizes how each scenario maps the counterparty:

RailIdentityInstrumentScenario
External ACH withdrawalOptional. For example, owner details from the linking institution.The accountholder's linked external bank accountACH withdrawal to a linked external account
ACH direct depositThe originator, identified by its ACH Company IDA stand-in instrument that's built from the ACH Company ID and routing numberACH direct deposit
Inbound online card paymentThe shopperThe cardInbound online card payment
Outbound card purchaseThe merchantNot neededOutbound card purchase

All payloads on this page are fictional. Transaction fields outside of counterparty follow the standard guidance in Transaction and Transaction Event Mapping.


ACH withdrawal to a linked external account

Your customer uses an Automated Clearing House (ACH) transfer to move $1,800 from their checking account to their own account at another bank, which they linked earlier. The counterparty instrument is that linked external account.

The following table lists the counterparty fields for this scenario:

Field typeValues
Event typetransaction
Minimum required counterparty fieldscounterparty.instrument.external_instrument_id
counterparty.instrument.instrument_type
Recommended counterparty fieldsrouting_number
account_number
account_holder_name
account_type

The following example shows the request:

{
    "event_type": "transaction",
    "event_lifecycle_id": "5071950e-adec-4f11-bd83-6e77af67d461",
    "event_status": "Completed",
    "data": {
        "external_transaction_id": "5071950e-adec-4f11-bd83-6e77af67d461",
        "external_account_id": "f3f49249-dc28-4f90-a5ae-c7978306d03b",
        "external_entity_id": "7dabe929-c4a3-44bf-86cd-75e9bb049a79",
        "timestamp": "2026-10-03T16:40:00Z",
        "transaction_created_date": "2026-10-03T16:40:00Z",
        "amount": -180000,
        "currency": "USD",
        "process_method": "ACH",
        "category": "Withdrawal",
        "is_triggered_by_user": true,
        "status": "Completed",
        "counterparty": {
            "instrument": {
                "external_instrument_id": "14aa4e71-9d3c-4dec-80a6-1f933d6c51e3",
                "instrument_type": "bank_account",
                "routing_number": "981234568",
                "account_number": "004455667788",
                "account_number_last4": "7788",
                "account_holder_name": "Johnny Appleseed",
                "account_type": "checking",
                "country_code": "US"
            }
        }
    }
}

You can omit identity in this case. The funds go to your own customer, who's already represented on the event by external_entity_id.

Legacy payload

In the legacy schema, the counterparty is keyed on the linked external account. Because 14aa4e71-9d3c-4dec-80a6-1f933d6c51e3 represents the bank account, use it as the instrument ID. The legacy account_name field contains the name on the account, so map it to account_holder_name.

"counterparty": {
    "external_counterparty_id": "14aa4e71-9d3c-4dec-80a6-1f933d6c51e3",
    "account_name": "Johnny Appleseed",
    "routing_number": "981234568",
    "account_number": "004455667788"
}

ACH direct deposit

Example Smoothie Shop pays your customer's wages by ACH direct deposit into the customer's checking account. Your institution is the receiving bank, and the NACHA file shows you the following information:

  • Example Smoothie Shop's company name and 10-digit ACH Company ID, from the file header
  • The routing number of Example Smoothie Shop's bank, from the file header
  • Your customer's account, from the entry detail record

The file doesn't include the account that Example Smoothie Shop paid from.

The ACH Company ID identifies the originator, so it maps to identity. The identity object doesn't have a routing number field, so you can also send an instrument object to keep the routing number. Because you don't know the originator's account number, you can base the instrument ID on the ACH Company ID and the routing number, such as ach-orig:4012345678:987654320. This instrument is a stand-in for Example Smoothie Shop's account at that bank. The ID stays the same for every deposit that Example Smoothie Shop sends through that bank.

The following table lists the counterparty fields for this scenario:

Field typeValues
Event typetransaction
Minimum required counterparty fieldscounterparty.identity.external_counterparty_identity_id
Recommended counterparty fieldsidentity.type
identity.identity_name
instrument.external_instrument_id, instrument.instrument_type, and instrument.routing_number, to keep the routing number

The following example shows the request:

{
    "event_type": "transaction",
    "event_lifecycle_id": "bef7e268-ffe9-46ab-a058-1ccace1d62e0",
    "event_status": "Completed",
    "data": {
        "external_transaction_id": "bef7e268-ffe9-46ab-a058-1ccace1d62e0",
        "external_account_id": "f3f49249-dc28-4f90-a5ae-c7978306d03b",
        "external_entity_id": "7dabe929-c4a3-44bf-86cd-75e9bb049a79",
        "timestamp": "2026-10-01T14:02:11Z",
        "transaction_created_date": "2026-10-01T14:02:11Z",
        "amount": 250000,
        "currency": "USD",
        "process_method": "ACH",
        "category": "Deposit",
        "status": "Completed",
        "counterparty": {
            "identity": {
                "external_counterparty_identity_id": "4012345678",
                "type": "business",
                "identity_name": "Example Smoothie Shop LLC"
            },
            "instrument": {
                "external_instrument_id": "ach-orig:4012345678:987654320",
                "instrument_type": "bank_account",
                "routing_number": "987654320"
            }
        }
    }
}

If Example Smoothie Shop's payroll later arrives through a different bank, send the same identity ID with a new instrument ID, such as ach-orig:4012345678:985550015. Your rules can then count Example Smoothie Shop once as a payer and still flag the switch to a new bank.

If you already onboarded Example Smoothie Shop to Alloy as a business Entity, link the identity to that Entity by sending the Entity's external ID in identity.external_entity_id, as shown in the following example:

"identity": {
    "external_counterparty_identity_id": "4012345678",
    "type": "business",
    "identity_name": "Example Smoothie Shop LLC",
    "external_entity_id": "e5121482-3929-4d22-a255-accb1a466884"
}

Alloy links the identity to the existing Entity and doesn't create a new one. Each identity can link to only one Entity. This field is different from the top-level external_entity_id, which identifies your customer.

📘

Note

An ACH Company ID identifies the originator, which is the business that sends or collects the payment through the ACH network. The originator isn't always the business that your customer recognizes. Payment processors often originate payments for many businesses under their own ACH Company IDs. If you key on ACH Company ID, every payment from those businesses rolls up to a single identity for the processor, and the stand-in instrument doesn't separate them either.

Legacy payload

In the legacy schema, external_counterparty_id contains the ACH Company ID, 4012345678. Use that value as the identity ID.

"counterparty": {
    "external_counterparty_id": "4012345678",
    "account_name": "Example Smoothie Shop LLC",
    "routing_number": "987654320"
}

Inbound online card payment

You monitor the business deposit account of Example Online Store. A shopper uses a credit card to buy $84.50 of goods on the store's website, and the funds arrive in the account that you monitor.

The counterparty identity is the shopper who uses the card, and the counterparty instrument is the card. When you send both IDs, your rules can catch patterns on either side. For example, rules can catch one shopper who pays with three different cards, or one stolen card that's used across ten shopper profiles.

The following table lists the counterparty fields for this scenario:

Field typeValues
Event typetransaction
Minimum required counterparty fieldscounterparty.identity.external_counterparty_identity_id
counterparty.instrument.external_instrument_id
counterparty.instrument.instrument_type
Recommended counterparty fieldsidentity.type, identity.name_first, identity.name_last, identity.email_address, identity.device
instrument.card_bin, instrument.card_last4, instrument.card_network, instrument.name_on_card, instrument.avs

The following example shows the request:

{
    "event_type": "transaction",
    "event_lifecycle_id": "5b4c8012-ede7-4d0c-bfb8-8309fadb8908",
    "event_status": "Completed",
    "data": {
        "external_transaction_id": "5b4c8012-ede7-4d0c-bfb8-8309fadb8908",
        "external_account_id": "f38b2ffc-80a4-4f5a-91c9-bc701e7ea419",
        "external_entity_id": "d7a7a3cc-8c3d-4f16-9293-de8fc88b2875",
        "timestamp": "2026-10-05T19:22:47Z",
        "transaction_created_date": "2026-10-05T19:22:47Z",
        "amount": 8450,
        "currency": "USD",
        "process_method": "Card",
        "category": "Deposit",
        "interaction_point": "Online Form",
        "status": "Completed",
        "is_approved": true,
        "counterparty": {
            "identity": {
                "external_counterparty_identity_id": "e4163207-d094-4996-82f0-ee99731c9452",
                "type": "person",
                "name_first": "Johnny",
                "name_last": "Appleseed",
                "email_address": "[email protected]",
                "address_postal_code": "94045",
                "address_country_code": "US",
                "device": {
                    "external_device_id": "70eb9a0a-9626-4ae6-85e8-18fac0433cbd",
                    "ip_address_v4": "203.0.113.24"
                }
            },
            "instrument": {
                "external_instrument_id": "6bad6be2-8e7a-46e9-9f19-950499dd251d",
                "instrument_type": "card",
                "card_bin": "400000",
                "card_last4": "1111",
                "card_network": "Visa",
                "name_on_card": "Johnny Appleseed",
                "expiration_month": "08",
                "expiration_year": "2029",
                "avs": "Y",
                "country_code": "US"
            }
        }
    }
}

The following details apply to this payload:

  • The amount is positive because funds come into the monitored account, and category is Deposit.
  • e4163207-d094-4996-82f0-ee99731c9452 is the shopper's ID in the store's system or payment processor. Use whichever ID stays stable across orders.
  • 6bad6be2-8e7a-46e9-9f19-950499dd251d is the card fingerprint that the processor assigns.
  • device describes the shopper's device at checkout, not a device that belongs to Example Online Store.
  • If the processor sends authorization and settlement separately, send both with the same external_transaction_id. Use Pending for the authorization and Completed for settlement.
Legacy payload

In the legacy schema, you choose either the card or the shopper as the key. If you key on the card, one shopper who pays with three different cards looks like three counterparties. If you key on the shopper, one stolen card that's used across ten shopper profiles looks like ten unrelated people.

"counterparty": {
    "external_counterparty_id": "[email protected]",
    "account_name": "Johnny Appleseed",
    "address_postal_code": "94045",
    "address_country_code": "US"
}

Outbound card purchase

Your customer uses their debit card to spend $42.99 at a hardware store. You don't know the merchant's bank account, so send an identity without an instrument.

The following table lists the counterparty fields for this scenario:

Field typeValues
Event typetransaction
Minimum required counterparty fieldscounterparty.identity.external_counterparty_identity_id
Recommended counterparty fieldsidentity.type
identity.identity_name
identity.merchant_identifier
identity.merchant_category_code
identity.mcc_description

The following example shows the request:

{
    "event_type": "transaction",
    "event_lifecycle_id": "59001ac9-4063-49bc-a5b0-0a2d35d14880",
    "event_status": "Completed",
    "data": {
        "external_transaction_id": "59001ac9-4063-49bc-a5b0-0a2d35d14880",
        "external_account_id": "f3f49249-dc28-4f90-a5ae-c7978306d03b",
        "external_entity_id": "7dabe929-c4a3-44bf-86cd-75e9bb049a79",
        "timestamp": "2026-10-06T12:05:30Z",
        "transaction_created_date": "2026-10-06T12:05:30Z",
        "amount": -4299,
        "currency": "USD",
        "process_method": "Card",
        "category": "Purchase",
        "interaction_point": "Point of Sale",
        "status": "Completed",
        "counterparty": {
            "identity": {
                "external_counterparty_identity_id": "1919e93a-d117-45ad-8988-93101c593af5",
                "type": "merchant",
                "identity_name": "Example Hardware Store",
                "merchant_identifier": "000839201",
                "merchant_category_code": "5251",
                "mcc_description": "Hardware Stores",
                "address_city": "Portland",
                "address_state": "OR",
                "address_country_code": "US"
            }
        }
    }
}

The card in this transaction belongs to your customer and is already associated with the monitored account. It isn't the counterparty instrument.

Legacy payload

In the legacy schema, the counterparty is typically keyed on the merchant descriptor. Move that value, or a more stable merchant ID if you have one, to identity.external_counterparty_identity_id. The merchant category fields also move to identity.


Did this page help you?