Originating RTP Credit Transfers

Real Time Payments (RTP) is a payment system developed by The Clearing House (TCH) that allows individuals and businesses to transmit funds that clear and settle immediately between US DDAs at different financial institutions. The Clearing House for additional information, including how RTP works, the technical specifications, network rules and other important RTP-related documentation.

In this tutorial we explore originating a credit transfer from a master account. We will also register relevant webhooks to receive status updates on our payment. Before starting this tutorial, please make sure you have the following:

  • API credentials
  • Partner ID
  • Master Account Number

This tutorial also assumes you have already opened a master account. If not, please complete the Opening an Account tutorial prior to completing this tutorial.

Get an Access Token

Before calling the COS API you must first request an access token. Once the access token has been obtained it is recommended that it is cached for the lifetime of the token. If the API starts to return a 401 response code, it is likely that your token has expired and you need to obtain a new one. More details can be found here.

Register Webhooks

Webhooks allow us to receive notifications when certain events occur within COS. After we originate our credit transfer, we will want to stay informed in order to track the payment through its lifecycle. For that you will need to call the webhook registration endpoint and indicate you wish to receive certain events. A few examples of RTP webhook registration calls are provided below.

To learn more about webhook registrations click here.
For more information on webhooks, see the Webhooks API section
For a full listing of available RTP webhook events click here.

The Rtp.Payment.Sent event will notify you in the event your payment has been sent over the RTP network and the funds are available in the creditor's (receiver's) bank account.

POST /webhooks/v1/registrations
{
  "partnerId": "00000000-0000-0000-0000-000000000000",
  "eventName": "Rtp.Payment.Sent",
  "registrationType": "Push", 
  "callbackUrl": "https://cos.yourcompanysite.com/rtp-events"
}

The Rtp.Payment.Received event notifies you in the event an inbound (received) credit transfer is received from another institution and has posted to your COS account:

POST /webhooks/v1/registrations
{
  "partnerId": "00000000-0000-0000-0000-000000000000",
  "eventName": "Rtp.Payment.Received",
  "registrationType": "Push", 
  "callbackUrl": "https://cos.yourcompanysite.com/rtp-events"
}

The Rtp.Payment.Rejected event will notify you in the event your outbound payment is rejected:

POST /webhooks/v1/registrations
{
  "partnerId": "00000000-0000-0000-0000-000000000000",
  "eventName": "Rtp.Payment.Rejected",
  "registrationType": "Push", 
  "callbackUrl": "https://cos.yourcompanysite.com/rtp-events"

Payments can be rejected by either of the following:

  • By Cross River Bank for compliance reasons
  • By the RTP Network
  • By the receiving institution

Before We Begin...

A Credit Transfer is the specific (and only) message in the RTP network which is used to transfer money between banks in real time. The party sending the Credit Transfer is referred to as the debtor while the party receiving the Credit Transfer is referred to as the creditor. The bank sending the Credit Transfer is referred to as the debtor FI while the bank receiving the Credit Transfer is referred to as the creditor FI. This is the equivalent to a “push” payment, meaning that one party (debtor) is sending funds to another party (creditor).

The receiving bank of the credit transfer (creditor) has 5 seconds to accept, reject or accept without posting.

  • Accept
    The receiving bank accepted the credit transfer and must post the funds to their customer’s account immediately. This means the debtor bank has been debited for the amount of the credited transfer and the creditor bank has been credited the amount of the credited transfer
  • Reject
    Typically one of the following:
    • The receiving bank did not accept the credit transfer, which means the funds were not debited from the debtor bank nor credited to the creditor bank. The creditor bank generally should only reject the payment under specific circumstances which are defined in the General Acceptance Requirement of the RTP Operating Rules.
    • The RTP system rejected the credit transfer because it was not formatted correctly or was missing mandatory fields
    • The RTP system rejected the credit transfer due to a system time-out. This is a rare outcome, but could either happen in the RTP system (isn’t able to send messages to the debtor and/or creditor banks in time), or the receiving bank (does not respond to the 5-second credit transfer SLA in time).
  • Accept without posting
    The debtor bank has been debited and the creditor bank has been credited, but the creditor bank has not yet posted the funds to their customer’s account yet. Banks rarely do this and are supposed to use this response only as advised in the RTP Operating Rules
    Refer to the Accept Without Posting section in the RTP Operating Rules for further guidance on the SLAs and other requirements for accept without posting.

There is no “pull” payment on the RTP network, meaning that one party cannot debit funds from another party’s DDA. The equivalent to a “pull” in RTP is the Request for Payment. For more information on the Request for Payment, refer to TCH’s website.

The outbound credit transfer can result in one of the following:

  • The creditor FI rejects the transfer
  • The creditor FI accepts the transfer
  • The creditor FI does not respond

🚧

Accepted Without Posting

There may be instances where the creditor FI will provide a response indicating that the transfer was accepted but funds were not yet deposited into the creditor's account, known as "Accepted Without Posting" (ACWP). The creditor FI can then subsequently deposit the funds into the creditor's account or reject the transfer.

Originating a Credit Transfer

In this scenario, we'll be simulating a person-to-person (P2P) transaction where Peter Griffin (debtor) is sending $150 to Cleveland Brown (creditor).

POST /v1/payments

{
  "accountNumber": "2553179843",
  "amount": 15000,
  "creditor": {
    "routingNumber": "011000138",
    "accountNumber": "456789000",
    "name": "Cleveland Brown",
    "addressStreetName": "Spooner St",
    "addressBuildingNumber": "34",
    "addressCity": "Quahog",
    "addressState": "RI",
    "addressPostalCode": "00093",
    "addressCountry": "US"
  },
  "remittanceData": "gift money"
}

🚧

It is highly recommended that you include an idempotency key in your request header. This will provide duplicate protection in the event of a failure. Read more about idempotency keys here.

Here's a sample response body based on the above request:

{
    "id": "7b5f4bfb-8595-452b-914e-ad9400f7b8e3",
    "originalPaymentId": "7b5f4bfb-8595-452b-914e-ad9400f7b8e3",
    "referenceId": "R242O0354Y055",
    "accountNumber": "2553179843",
    "amount": 15000,
    "direction": "Outbound",
    "status": "Created",
    "paymentType": "CreditTransfer",
    "source": "Api",
    "transactionAccountContext": "NotSubmitted",
    "debtor": {
        "routingNumber": "021214891",
        "accountNumber": "2553179843",
        "name": "Peter M Griffin",
        "addressStreetName": "Spooner Street",
        "addressBuildingNumber": "31",
        "addressCity": "Quahog",
        "addressState": "RI",
        "addressPostalCode": "00093",
        "addressCountry": "US"
    },
    "creditor": {
        "routingNumber": "011000138",
        "accountNumber": "456789000",
        "name": "Cleveland Brown",
        "addressStreetName": "Spooner St",
        "addressBuildingNumber": "34",
        "addressCity": "Quahog",
        "addressState": "RI",
        "addressPostalCode": "00093",
        "addressCountry": "US"
    },
    "network": {
        "businessMessageId": "B20210830021214273T1BNSI93853549430",
        "messageId": "M20210830021214273T1BEML46024873029",
        "createdAt": "2021-08-30T11:01:55.7404545-04:00",
        "numberOfTransactions": 1,
        "interbankSettlementAmount": 15000,
        "currency": "USD",
        "interbankSettlementDate": "2021-08-30",
        "settlementMethod": "CLRG",
        "clearingSystemCode": "TCH",
        "instructionId": "20210830021214273T1B4S0534677157734",
        "endToEndId": "9a2638dc8cbe48f18d8cad9400f7b8e3",
        "transactionId": "20210830021214273T1B4S0534677157734",
        "fromParticipantId": "021214273T1",
        "toParticipantId": "990000001S1",
        "instructingRoutingNumber": "021214891",
        "instructedRoutingNumber": "011000138",
        "headerCreationDate": "2021-08-30T11:01:55.7404545-04:00",
        "messageCreationDateTime": "2021-08-30T11:01:55.7404545-04:00"
    },
    "serviceLevelCode": "SDVA",
    "localInstrumentProprietary": "STANDARD",
    "categoryPurpose": "CONSUMER",
    "currency": "USD",
    "chargeBearer": "SLEV",
    "remittanceData": "gift money",
    "wasRefunded": false,
    "wasPaid": false,
    "createdAt": "2021-08-30T11:01:55.7404545-04:00",
    "productId": "13362d99-f04e-455b-9363-abc10151c27c",
    "partnerId": "bd7a716f-6349-43ef-89cd-aa2200f15977",
    "lastModifiedAt": "2021-08-30T11:01:55.7414531-04:00",
    "sendAttemptCount": 0,
    "discounts": [],
    "awaitingResponse": false
}

Every request received by COS will contain a request-id value. This is a unique identifier for your request and is useful when troubleshooting issues with our support team. It is recommended you log this identifier.

{
  "access-control-allow-credentials": "true",
  "access-control-allow-origin": "https://sandbox.crbcos.com",
  "cache-control": "no-cache",
  "content-length": "1855",
  "content-type": "application/json; charset=utf-8",
  "date": "Mon, 30 Aug 2021 15:01:55 GMT",
  "expires": "-1",
  "pragma": "no-cache",
  "request-id": "5329cb29-ce2c-4586-8767-ad9400f7b8dc",
  "server": "Microsoft-IIS/8.5",
  "x-aspnet-version": "4.0.30319",
  "x-frame-options": "SAMEORIGIN",
  "x-powered-by": "ASP.NET"
}

Once the payment is released to the network, you'll receive a webhook event which will contain the payment ID within the resources section of the event body:

{
  "id": "7af80bc3-4f1c-4842-b60e-ad9400fb59db",
  "eventName": "Rtp.Payment.Sent",
  "status": "Pending",
  "partnerId": "bd7a716f-6349-43ef-89cd-aa2200f15977",
  "createdAt": "2021-08-30T11:15:08.623-04:00",
  "resources": [
    "rtp/v1/payments/7b5f4bfb-8595-452b-914e-ad9400f7b8e3"
  ],
  "details": []
}

Use that payment ID with the GET /v1/payments/{id} endpoint to query the details of the payment:

GET /v1/payments/{id}

{
    "id": "7b5f4bfb-8595-452b-914e-ad9400f7b8e3",
    "originalPaymentId": "7b5f4bfb-8595-452b-914e-ad9400f7b8e3",
    "referenceId": "R242O0354Y055",
    "accountNumber": "2553179843",
    "amount": 15000,
    "operatorCoreTransactionId": "2da7c99c-5804-4757-98c1-ad9400fa6c2c",
    "memoPostId": "7b5f4bfb-8595-452b-914e-ad9400f7b8e3",
    "memoPostRemovedAt": "2021-08-30T11:14:44.973-04:00",
    "direction": "Outbound",
    "status": "Completed",
    "paymentType": "CreditTransfer",
    "source": "Api",
    "transactionAccountContext": "Complete",
    "rtpTransactionStatus": "ACTC",
    "debtor": {
        "routingNumber": "021214891",
        "accountNumber": "2553179843",
        "name": "Peter M Griffin",
        "addressStreetName": "Spooner Street",
        "addressBuildingNumber": "31",
        "addressCity": "Quahog",
        "addressState": "RI",
        "addressPostalCode": "00093",
        "addressCountry": "US"
    },
    "creditor": {
        "routingNumber": "011000138",
        "accountNumber": "456789000",
        "name": "Cleveland Brown",
        "addressStreetName": "Spooner St",
        "addressBuildingNumber": "34",
        "addressCity": "Quahog",
        "addressState": "RI",
        "addressPostalCode": "00093",
        "addressCountry": "US"
    },
    "network": {
        "messageDefId": "pacs.008.001.08",
        "businessMessageId": "B20210830021214273T1BQPZ97287285414",
        "messageId": "M20210830021214273T1BEML46024873029",
        "createdAt": "2021-08-30T11:01:55.74-04:00",
        "numberOfTransactions": 1,
        "interbankSettlementAmount": 15000,
        "currency": "USD",
        "interbankSettlementDate": "2021-08-30",
        "settlementMethod": "CLRG",
        "clearingSystemCode": "TCH",
        "instructionId": "20210830021214273T1B4S0534677157734",
        "endToEndId": "9a2638dc8cbe48f18d8cad9400f7b8e3",
        "transactionId": "20210830021214273T1B4S0534677157734",
        "clearingSystemRef": "001",
        "fromParticipantId": "021214273T1",
        "toParticipantId": "990000001S1",
        "instructingRoutingNumber": "021214891",
        "instructedRoutingNumber": "011000138",
        "headerCreationDate": "2021-08-30T11:11:45.373-04:00",
        "messageCreationDateTime": "2021-08-30T11:01:55.74-04:00"
    },
    "confirmedStatus": {
        "messageStatusReportId": "2da7c99c-5804-4757-98c1-ad9400fa6c2c",
        "acceptedDateTime": "2021-08-30T11:11:45.4-04:00",
        "transactionStatus": "ACTC"
    },
    "serviceLevelCode": "SDVA",
    "localInstrumentProprietary": "STANDARD",
    "categoryPurpose": "CONSUMER",
    "currency": "USD",
    "chargeBearer": "SLEV",
    "remittanceData": "gift money",
    "wasRefunded": false,
    "wasPaid": false,
    "createdAt": "2021-08-30T11:01:55.74-04:00",
    "completedAt": "2021-08-30T11:14:45.05-04:00",
    "processingStartedAt": "2021-08-30T11:11:45.247-04:00",
    "postingCode": "OK",
    "productId": "13362d99-f04e-455b-9363-abc10151c27c",
    "partnerId": "bd7a716f-6349-43ef-89cd-aa2200f15977",
    "lastModifiedAt": "2021-08-30T11:14:45.0505825-04:00",
    "sentAt": "2021-08-30T11:11:45.3733333-04:00",
    "sendAttemptCount": 1,
    "result": {
        "code": "OK"
    },
    "discounts": [],
    "awaitingResponse": false
}

The response body above reflects a payment status of Completed and an rtpTransactionStatus of ACTC. Visit the RTP Reference page more information on RTP payment statuses.

📘

Simulate a Rejection Response to an Outbound Credit Transfer

You can simulate receiving a reject response from a creditor by prefixing the creditor's name with "reject:". For example:

{
"accountNumber": "2333844997",
"amount": 113,
"creditor": {
"routingNumber": "021000021",
"accountNumber": "5646161",
"name": "reject: Cleveland Brown",
"addressStreetName": "Spooner St",
"addressBuildingNumber": "34",
"addressCity": "Quahog",
"addressState": "RI",
"addressPostalCode": "00093",
"addressCountry": "US"
},
"remittanceData": "string",
"clientIdentifier": "string"
}

Credit Transfer Using Ultimate Debtor

In this example, we'll be illustrating the use of the ultimate debtor fields within the payment transaction. Initech, a customer of Acme Co, wants to send $100 to their customer Globex Corp. Acme Co will be originating the $100 payment from their COS account. This means that Acme Co is the debtor, Initech is the ultimate debtor, and Globex Corp is the creditor. Here's what that request would look like:

{
  "accountNumber": "2342123458",
  "amount": 10000,
  "debtor": {
    "accountNumber": "2342123458",
    "name": "Acme Co",
    "addressStreetName": "Any St",
    "addressBuildingNumber": "123",
    "addressCity": "Anytown",
    "addressState": "NY",
    "addressPostalCode": "11101",
    "addressCountry": "US"
  },
  "ultimateDebtor": {
    "name": "Initech",
    "addressStreetName": "Office Way",
    "addressBuildingNumber": "555",
    "addressCity": "Sometown",
    "addressState": "MA",
    "addressPostalCode": "03741",
    "addressCountry": "US"
  },
  "creditor": {
    "routingNumber": "021000021",
    "accountNumber": "222354852",
    "name": "Globex Corp",
    "addressStreetName": "World Way",
    "addressBuildingNumber": "15",
    "addressCity": "New York",
    "addressState": "NY",
    "addressPostalCode": "10013",
    "addressCountry": "US"
  },
  "remittanceData": "Invoice T741"
  },
}