Payments Processing
Initiating credit transfers (Wires, ACH, RTP, FedNow) through ORCA. The workflow covers recipient identification, account validation, balance checks, payment initiation, status retrieval, and cancellation. ORCA's role is initiation only — the actual movement of funds across payment rails (RTP network, FedNow service, ACH operators, Fedwire) happens downstream at the Core or at a Payment Manager (e.g., PortX Payment Manager).
Business context
Payment orchestrators handling real-time payment networks (RTP, FedNow), batch ACH, and wire transfers need a normalized way to push payment instructions into many different financial institutions. Each Core (Fiserv, FIS, Jack Henry, etc.) speaks its own dialect and exposes payment posting differently; building a bespoke connector per Core multiplies the engineering effort.
ORCA exposes /credit-transfers as a single normalized REST resource based on ISO 20022 pain.001 semantics. The orchestrator submits a payment instruction; ORCA translates and forwards to the target Core or downstream Payment Manager, which then handles the rails-side processing (clearing, settlement, network messaging).
ORCA initiation boundary
The ORCA /credit-transfers endpoint is a payment initiation interface — it accepts the instruction, validates basic structure, and forwards it to the system that owns settlement. ORCA does not:
- Send messages on the RTP network or to the FedNow service
- Perform clearing or settlement
- Manage the lifecycle states beyond what the downstream system reports back
- Hold funds in any form (ORCA is stateless)
Use this page for what to send to ORCA. For rails-side behavior, consult the Payment Manager's documentation or the network specifications directly.
Scope
In-scope for this use case:
- Recipient identification —
Person,Organization, andAccountlookups by various identifiers (account number, phone number, email, tax ID, etc.) - Account validation — verifying that the recipient account exists, is
Active, and can receive payments - Balance checks — pre-posting available-funds inspection
- Credit transfer initiation — submitting payment instructions for all
PaymentMethodvalues supported by the Core - Payment status retrieval — querying the lifecycle status (
Received,AcceptedTechnicalValidation,AcceptedSettlementCompleted,Rejected, etc.) - Transaction history lookup — finding posted transactions on the recipient account
- Cancellation — requesting cancellation of a previously initiated transfer (where supported by the rail)
Out-of-scope (handled elsewhere):
- Rails network communication — RTP, FedNow, Fedwire, ACH network operations happen at the Core or downstream Payment Manager
- Clearing and settlement — neither modeled nor performed by ORCA
- Payment validation against scheme rules — RTP message format conformance, FedNow business rules, NACHA validation are handled downstream
- Fraud screening, OFAC/AML scanning — performed by the Core or a dedicated fraud system, not by ORCA
- Outbound payment origination from the FI's customer accounts — this use case covers receiving payments into the FI; outbound origination uses the same
/credit-transfersendpoint but with different debtor/creditor framing and rail-specific concerns out of scope here
Architecture context
sequenceDiagram
autonumber
participant FT as Fintech<br/>(Payment Orchestrator)
participant ORCA as ORCA API
participant Core as FI Core /<br/>Payment Manager
Note over FT: Inbound RTP/FedNow message arrives<br/>from the network at the orchestrator
FT->>ORCA: GET /persons OR /accounts (lookup by identifier)
ORCA->>Core: Recipient lookup
Core-->>ORCA: Match
ORCA-->>FT: 200 [Descriptors]
FT->>ORCA: GET /accounts/{accountId} (account detail + status)
ORCA->>Core: Account read
Core-->>ORCA: Account data
ORCA-->>FT: 200 {Account}
opt Pre-posting balance check
FT->>ORCA: GET /accounts/{accountId}/balances
ORCA->>Core: Balance read
Core-->>ORCA: Balances
ORCA-->>FT: 200 [Balance[]]
end
Note over FT,Core: Step 4: Initiate the credit transfer
FT->>ORCA: POST /credit-transfers (CreditTransferInitiation)
ORCA->>Core: Forward payment instruction
Note over Core: Core/Payment Manager handles:<br/>scheme validation, OFAC,<br/>rails messaging, settlement
Core-->>ORCA: paymentId, initial status
ORCA-->>FT: 202 {paymentId, status: "Received"}
opt Status polling
FT->>ORCA: GET /credit-transfers/{paymentId}
ORCA->>Core: Status read
Core-->>ORCA: Updated status
ORCA-->>FT: 200 {CreditTransfer}
end
opt Cancellation (where supported by the rail)
FT->>ORCA: POST /credit-transfers/{paymentId}/cancellation
ORCA->>Core: Forward cancellation request
Core-->>ORCA: Cancellation status
ORCA-->>FT: 202 {status: "AcceptedCancellationRequest"}
end
opt Posted transaction lookup
FT->>ORCA: GET /accounts/{accountId}/transactions
ORCA->>Core: Transaction read
Core-->>ORCA: Transactions
ORCA-->>FT: 200 [Transaction[]]
end
Endpoints used
| Step | Method | Path | Purpose |
|---|---|---|---|
| 1 | GET |
/persons |
Recipient lookup (personal) |
| 1 | GET |
/organizations |
Recipient lookup (commercial) |
| 1 | GET |
/accounts |
Account lookup by account number, identifier, etc. |
| 1 | GET |
/accounts/{accountId}/identifiers |
Retrieve alternate identifiers (phone, email aliases) |
| 2 | GET |
/persons/{personId} |
Recipient detail |
| 2 | GET |
/accounts/{accountId} |
Account detail + status |
| 2 | GET |
/accounts/{accountId}/owners |
Verify ownership |
| 3 | GET |
/accounts/{accountId}/balances |
Pre-posting balance check |
| 4 | POST |
/credit-transfers |
Initiate the credit transfer |
| 5 | GET |
/credit-transfers |
List credit transfers (with filters) |
| 5 | GET |
/credit-transfers/{paymentId} |
Retrieve a specific transfer |
| 5 | POST |
/credit-transfers/{paymentId}/cancellation |
Request cancellation |
| 5 | GET |
/accounts/{accountId}/transactions |
Posted transaction history |
Common headers
| Header | Purpose |
|---|---|
idempotencyId |
Critical for POST /credit-transfers. Network retries without idempotency cause duplicate payments. |
servicerId |
Identifies the servicing institution. |
Step 1 — Identify the recipient
When an inbound payment arrives at the orchestrator, the message carries identifiers — account number, routing number, phone, email, tax ID, or a token alias. Resolve those to an ORCA personId + accountId pair.
By account number
GET /accounts?accountNumber.eq=1234567891 HTTP/1.1
Host: api.portx.io
Authorization: Bearer <jwt>
servicerId: bank-001
[
{
"accountId": "acct-003-chk-1f2e3d4c",
"accountNumber": "1234567891",
"accountType": "Checking",
"status": "Active",
"routingNumbers": [
{ "number": "021000021", "type": "ABA" }
]
}
]
By alternate identifier (RTP/FedNow alias)
For RTP and FedNow, recipients are often identified by a token — a phone number, email, or proxy ID maintained in a directory. The Core may store these aliases as Account.identifiers[] or Account.alternativeNames[].
GET /accounts?identifier.eq=%2B15125550100 HTTP/1.1
or
GET /accounts/{accountId}/identifiers HTTP/1.1
By person + person's accounts
GET /persons?phoneNumber.eq=%2B15125550100 HTTP/1.1
Then resolve the person's accounts:
GET /persons/{personId}/accounts HTTP/1.1
Step 2 — Retrieve account detail and verify status
Before posting, confirm the account is in a state that can receive funds.
GET /accounts/acct-003-chk-1f2e3d4c HTTP/1.1
{
"accountId": "acct-003-chk-1f2e3d4c",
"accountNumber": "1234567891",
"accountType": "Checking",
"ownershipType": "JointAccount",
"status": "Active",
"currency": "USD",
"parties": [
{ "partyId": "person-7f3a8c92-4b1e-4d2f-9a0c-1b2c3d4e5f60", "partyAccountRole": "AccountOwner", "name": "Jane M Doe" }
]
}
The recipient account is good to receive a payment when status: "Active" and no blocking restrictions are present. Other relevant statuses for payments: Closed, Dormant, Restricted, Frozen — all should reject inbound credits.
Step 3 — Pre-posting balance check (optional)
For inbound payments to a deposit account, balance checks are usually unnecessary — credits don't fail on insufficient funds. The check is more relevant for outbound payments (debit-side), and for inbound payments that have a contingent return (e.g., a deposit that needs to immediately fund another transaction).
GET /accounts/acct-003-chk-1f2e3d4c/balances HTTP/1.1
[
{ "name": "AvailableBalance", "amount": "2450.18", "currency": "USD", "creditDebitIndicator": "Credit" },
{ "name": "CurrentBalance", "amount": "2450.18", "currency": "USD", "creditDebitIndicator": "Credit" },
{ "name": "AvailableOverdraft","amount": "500.00", "currency": "USD" }
]
Step 4 — Initiate the credit transfer
POST /credit-transfers → 202 Accepted with CreditTransfer in the body.
Key request fields (CreditTransferInitiation)
| Field | Required | Description |
|---|---|---|
amount |
yes | Decimal string (e.g., "100.00"). |
debtorAccountId |
yes | The account being debited (the source of funds). |
creditorAccountId |
conditional | The account being credited. For inbound payments where the creditor is at the FI, this is the recipient's accountId. |
paymentMethod |
yes | The means of payment — see table below. |
paymentType |
recommended | OnUs, ACH, Wire, NotOnUs, LiquidityTransfer. |
currency |
no | Defaults to USD. |
valueDate |
no | Date the payment value is effective. |
dueDate |
no | Date the payment is due to be made. |
purpose |
no | Free-text description of why the payment is being made. |
paymentProcessing |
recommended | instructionPriority, serviceLevel, clearingChannel, localInstrument, categoryPurpose. This is where rail-specific framing lives. |
chargeBearerType |
no | Who pays fees: Debtor, Creditor, Shared, ServiceLevel. |
creditor |
recommended | Full PartyIdentification for the receiving party. |
debtor |
recommended | Full PartyIdentification for the sending party. |
parties[] |
no | Additional parties in the payment chain (intermediary banks, ultimate creditor/debtor). |
remittanceInformation |
no | Invoice number, document type, structured remittance data. |
identifiers[] |
recommended | Carry external identifiers (e.g., RTP messageId, FedNow UETR). |
paymentMethod values
| Value | Use |
|---|---|
CreditTransfer |
Generic credit transfer; pair with paymentType for rail. |
ACHCredit |
ACH credit entry. |
ACHDebit |
ACH debit entry (less common for "credit transfer" framing). |
FedWire |
Fedwire transfer. |
EFTCredit / EFT |
Electronic funds transfer. |
Check, ECheck |
Check / electronic check. |
Cash |
Cash deposit / withdrawal entry. |
MoneyOrder, Coupon, LockBox, RemoteCapture |
Specialized payment forms. |
Where does RTP and FedNow fit?
Neither RTP nor FedNow appears as a standalone paymentMethod enum value. They are conveyed through the combination:
paymentMethod: "CreditTransfer"paymentType: "OnUs"(intra-bank) or appropriate variantpaymentProcessing.clearingChannel: "RTGS"or rail-specific valuepaymentProcessing.localInstrument: "RTP"/"FedNow"paymentProcessing.serviceLevel: "URGP"(urgent) or similar
Confirm with the integration team which localInstrument codes the target Core/Payment Manager expects.
ISO 20022 — debtor / creditor
In ISO 20022 payment messages, debtor is the party whose account is debited (paying), and creditor is the party whose account is credited (receiving). This holds regardless of who initiates the message — for an inbound RTP payment arriving at your FI, the debtor is the external customer at the originating bank, and the creditor is your FI's customer.
Inbound RTP payment example
POST /credit-transfers HTTP/1.1
Host: api.portx.io
Content-Type: application/json
idempotencyId: e8f9a0b1-c2d3-4e5f-6a7b-8c9d0e1f2a3b
servicerId: bank-001
{
"amount": "150.00",
"currency": "USD",
"paymentMethod": "CreditTransfer",
"paymentType": "NotOnUs",
"debtorAccountId": "ext-acct-originating-bank-987654",
"creditorAccountId": "acct-003-chk-1f2e3d4c",
"paymentProcessing": {
"instructionPriority": "High",
"serviceLevel": "URGP",
"clearingChannel": "RTGS",
"localInstrument": "RTP",
"categoryPurpose": "SUPP"
},
"purpose": "Reimbursement",
"debtor": {
"name": {
"name": "Alex P Customer"
},
"account": {
"accountNumber": "9988776655",
"agent": {
"bankIdentifier": "121000248",
"name": "Wells Fargo Bank, NA"
}
}
},
"creditor": {
"name": {
"name": "Jane M Doe"
}
},
"identifiers": [
{ "schemeName": "EndToEndId", "number": "RTP-2026-051900001234" },
{ "schemeName": "UETR", "number": "e8f9a0b1-c2d3-4e5f-6a7b-8c9d0e1f2a3b" }
],
"remittanceInformation": {
"remittanceNumber": "INV-2026-04823",
"documentType": "CommercialInvoice"
}
}
{
"paymentId": "pay-2a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d",
"status": "Received",
"amount": "150.00",
"currency": "USD",
"paymentMethod": "CreditTransfer",
"paymentType": "NotOnUs",
"debtorAccountId": "ext-acct-originating-bank-987654",
"creditorAccountId": "acct-003-chk-1f2e3d4c",
"date": "2026-05-19T14:42:08Z",
"identifiers": [
{ "schemeName": "EndToEndId", "number": "RTP-2026-051900001234" }
]
}
FedNow payment example
Identical structure; differs in paymentProcessing.localInstrument:
{
"amount": "75.50",
"paymentMethod": "CreditTransfer",
"paymentType": "NotOnUs",
"debtorAccountId": "ext-acct-originating-bank-447712",
"creditorAccountId": "acct-003-chk-1f2e3d4c",
"paymentProcessing": {
"instructionPriority": "High",
"serviceLevel": "URGP",
"localInstrument": "FedNow",
"categoryPurpose": "CASH"
},
"purpose": "Personal transfer",
"identifiers": [
{ "schemeName": "EndToEndId", "number": "FN-2026-051900447712" }
]
}
ACH credit example
{
"amount": "1250.00",
"paymentMethod": "ACHCredit",
"paymentType": "ACH",
"debtorAccountId": "ext-acct-payroll-employer-001",
"creditorAccountId": "acct-003-chk-1f2e3d4c",
"paymentProcessing": {
"categoryPurpose": "SALA",
"localInstrument": "PPD"
},
"purpose": "Payroll",
"valueDate": "2026-05-22",
"creditor": {
"name": { "name": "Jane M Doe" }
},
"debtor": {
"name": { "name": "Acme Industries LLC" },
"agent": { "bankIdentifier": "021000089" }
},
"identifiers": [
{ "schemeName": "TraceNumber", "number": "021000089-1234567890" }
]
}
Step 5 — Status and history
Retrieve a specific payment
GET /credit-transfers/{paymentId}
{
"paymentId": "pay-2a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d",
"status": "AcceptedSettlementCompleted",
"statusReason": "Settled",
"amount": "150.00",
"currency": "USD",
"paymentMethod": "CreditTransfer",
"creditorAccountId": "acct-003-chk-1f2e3d4c",
"debtorAccountId": "ext-acct-originating-bank-987654",
"settlement": {
"postingType": "HardPosted",
"time": "2026-05-19T14:42:11.481Z"
},
"date": "2026-05-19T14:42:08Z"
}
Payment status lifecycle
ISO 20022 pain.002 and pacs.002 define a rich set of status values. The most commonly seen for credit transfers:
| Status | Meaning |
|---|---|
Received |
ORCA accepted the instruction; not yet validated by the Core. |
AcceptedTechnicalValidation |
Schema and basic validation passed at the Core. |
AcceptedCustomerProfile |
Customer-side validation (limits, profile rules) passed. |
AcceptedSettlementInProcess |
Settlement initiated on the rail. |
AcceptedSettlementCompleted |
Funds settled at the destination. Terminal success state. |
Pending |
Awaiting downstream action. |
Rejected |
Validation failure or rail-side rejection. statusReason carries why. |
AcceptedCancellationRequest |
Cancellation request accepted (pending action). |
RejectedCancellationRequest |
Cancellation rejected. |
statusReason values (common)
AbortedClearingTimeout, AbortedSettlementTimeout, ErrorCreditorAgent, OfflineAgent, InvalidAccount, AccountClosed, InsufficientFunds, DuplicateMessage, FraudSuspected, BlockedByPolicy, ReturnedByBeneficiary.
List with filters
GET /credit-transfers?status.eq=Rejected&creationDate.gte=2026-05-19&creditorAccountId.eq=acct-003-chk-1f2e3d4c HTTP/1.1
Cancellation
POST /credit-transfers/{paymentId}/cancellation
{
"cancellationReason": "Duplicate instruction sent by orchestrator at 14:42:09"
}
Cancellation is only meaningful for payments still in flight. RTP and FedNow are settled within seconds; once AcceptedSettlementCompleted, cancellation is not possible — recovery must use a return transaction or business resolution between counterparties.
Posted transaction history
GET /accounts/acct-003-chk-1f2e3d4c/transactions?paymentId.eq=pay-2a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d HTTP/1.1
Returns the posted ledger entry on the recipient account with full transaction metadata.
Error handling
| Status | When |
|---|---|
400 Bad Request |
Schema validation failure: missing amount, debtorAccountId, or paymentMethod. |
404 Not Found |
debtorAccountId or creditorAccountId references an unknown account. |
409 Conflict |
Idempotency collision — same idempotencyId with different request body. Treat as a duplicate prevention success, not an error. |
422 Unprocessable Entity |
Core or Payment Manager rejected the instruction on business grounds (account closed, daily limit exceeded, OFAC hit). |
500 Internal Server Error |
Unexpected ORCA or Core failure. |
502 Bad Gateway |
Core or Payment Manager unreachable. |
503 Service Unavailable |
Payment Manager congestion; safe to retry with the same idempotencyId. |
Edge cases and caveats
Idempotency is non-negotiable
Always send idempotencyId on POST /credit-transfers. Network retries without it create duplicate payments. The orchestrator should generate a UUID per intended payment, keep it associated with the source message (RTP/FedNow EndToEndId), and use it for every retry of that payment.
202 ≠ settled
POST /credit-transfers returns 202 Accepted — meaning the instruction is forwarded to the Core/Payment Manager. It does not mean the funds have settled or that the rails-side processing succeeded. Poll GET /credit-transfers/{paymentId} or subscribe to status webhooks (if configured) to track the lifecycle through to AcceptedSettlementCompleted.
ORCA does not perform OFAC, sanctions, or fraud screening
Compliance screening happens at the Core or in a dedicated fraud system. A Rejected status with statusReason: "BlockedByPolicy" or "FraudSuspected" indicates a downstream block, not an ORCA rule.
RTP/FedNow timing
Real-time rails complete in seconds. The full lifecycle (Received → AcceptedSettlementCompleted) may finish before the orchestrator polls for status. Consider this when designing the polling cadence — a 404 is unlikely, but a fully-terminal status on the first poll is common.
Cancellation is best-effort
Once a payment reaches AcceptedSettlementCompleted on an irrevocable rail (RTP, FedNow, Fedwire), it cannot be cancelled. The orchestrator must initiate a separate return or refund transaction.
Currency
The ORCA spec supports multi-currency via currency, but RTP, FedNow, ACH, and Fedwire are USD-only domestic rails. Cross-border payments are out of scope for this use case.
Posting type — memo vs hard
settlement.postingType: "MemoPosted" indicates a provisional posting (visible in balance, not yet hard-posted). "HardPosted" is the final ledger commit. Cores differ in when they transition; consult the Core's documentation for posting cycle behavior.
Related building blocks
- Account Opening — Recipient accounts must exist before they can receive a payment. See Account Opening.
- Internal Transfers — For intra-bank money movement (account ↔ account at the same FI), use
/internal-transfersinstead. See Customer Service. - PortX Payment Manager — Downstream component that handles rails messaging and settlement. Refer to its documentation for rails-side behavior.