Clearing House Request To Pay Blueprint
Request-To-Pay (RTP) and refunds for clearing house payments — outbound/inbound RTP initiation, cancellation, and refund processing
| Feature | clearing-house-request-to-pay |
| Category | Integration |
| Version | 1.0.0 |
| Tags | rtp, request-to-pay, refund, payments, clearing-house |
| YAML Source | View on GitHub |
| JSON API | clearing-house-request-to-pay.json |
Actors
| ID | Name | Type | Description |
|---|---|---|---|
partner_system | Partner System | system | Financial institution integrating with Electrum for PayShap RTP |
payment_platform | Payment Orchestration Platform | external | Clearing house platform that orchestrates RTP messaging |
clearing_house | Clearing House Operator | external | Clearing house scheme operator managing the real-time payments network |
debtor_bank | Debtor Bank | external | Financial institution holding the debtor’s account |
Fields
| Name | Type | Required | Label | Description |
|---|---|---|---|---|
uetr | token | Yes | UETR | |
end_to_end_identification | text | Yes | End-to-End Identification | Validations: maxLength |
creditor_account_number | text | Yes | Creditor Account Number | |
debtor_party_name | text | Yes | Debtor Party Name | Validations: maxLength |
debtor_party_identification | text | No | Debtor Party Identification | |
amount_value | number | No | Amount Value | |
amount_currency | text | Yes | Amount Currency | |
amount_range_min | number | No | Amount Range Minimum | |
amount_range_max | number | No | Amount Range Maximum | |
expiry_date_time | datetime | No | Expiry Date/Time | |
remittance_information | text | No | Remittance Information | |
rtp_status | select | No | RTP Status | |
cancellation_reason | text | No | Cancellation Reason | |
refund_amount | number | No | Refund Amount |
States
State field: rtp_status
Values:
| State | Initial | Terminal |
|---|---|---|
initiated | Yes | |
presented | ||
paid | Yes | |
rejected | Yes | |
cancelled | Yes | |
expired | Yes |
Transitions:
| Name | From | To | Actor | Condition |
|---|---|---|---|---|
initiated | presented | electrum | ||
presented | paid | debtor_bank | ||
presented | rejected | debtor_bank | ||
presented | cancelled | partner_system | ||
presented | expired | electrum |
Rules
- scheme:
- payshap_only: Only the ZA_RPP (PayShap) scheme is supported for RTP
- async_model: All RTP processing is asynchronous — requests and responses travel on separate HTTP calls
- end_to_end_time_limit: PayShap mandates a 10-second end-to-end time limit for message processing
- amount:
- exact_or_range: An RTP can specify either an exact amount (amount_value) or an amount range (amount_range_min / amount_range_max), not both
- currency_required: amount_currency is always required (ISO 4217)
- cancellation:
- already_paid: Cancellation may fail if the debtor has already paid — status ALREADY_PAID returned
- before_expiry: Cancellations are only meaningful before the RTP expires
- refund:
- original_amount_default: If refund_amount is omitted, the original transaction amount is used
- original_debtor_default: If debtor details are omitted, the creditor of the original transaction becomes the debtor of the refund
- prior_transaction_required: A refund must reference a prior successful financial transaction
- expiry:
- configurable: RTP expiry is configurable via the expiryDateTime field (ISO 8601)
SLA
| Scope | Max Duration | Escalation |
|---|---|---|
| payshap_end_to_end | 10s |
Flows
Outbound_rtp
Partner initiates a request-to-pay to a debtor via PayShap
- POST to Electrum /transactions/outbound/request-to-pay with RTP details (partner_system)
- Validate and forward RTP through PayShap (ZA_RPP) scheme (electrum)
- Route RTP to debtor bank (bankservafrica)
- Present RTP to debtor for authorisation (debtor_bank)
- POST response to partner’s /transactions/outbound/request-to-pay-response (electrum)
Inbound_rtp
Electrum delivers an inbound RTP from another institution to the partner
- POST to partner’s /transactions/inbound/request-to-pay (electrum)
- Process inbound RTP and determine response (partner_system)
- POST response to Electrum /transactions/inbound/request-to-pay-response (partner_system)
Outbound_cancellation
Partner cancels a previously initiated outbound RTP
- POST to Electrum /transactions/outbound/request-to-pay/cancellation-request (partner_system)
- Forward cancellation through PayShap scheme (electrum)
- POST cancellation response to partner’s callback endpoint (electrum)
Inbound_cancellation
Electrum delivers a cancellation request for an inbound RTP
- POST to partner’s /transactions/inbound/request-to-pay/cancellation-request (electrum)
- Process cancellation and respond (partner_system)
Inbound_status_request
Electrum requests the status of an inbound RTP from the partner
- POST to partner’s /transactions/inbound/request-to-pay/status-request (electrum)
- Return current RTP status (partner_system)
Refund
Partner initiates a refund for a prior successful transaction
- POST to Electrum /transactions/outbound/refund-initiation (partner_system)
- Process refund through PayShap scheme (electrum)
- POST refund response to partner’s /transactions/outbound/refund-initiation-response (electrum)
Outcomes
Outbound_rtp_initiated (Priority: 1)
Given:
- Partner system is authenticated
uetr(input) existscreditor_account_number(input) existsamount_currency(input) exists
Then:
- call_service target:
electrum_chp.outbound_rtp— POST /transactions/outbound/request-to-pay - transition_state field:
rtp_statusfrom:initiatedto:presented - emit_event event:
rtp.outbound.initiated
Result: Outbound RTP submitted to Electrum and forwarded to debtor bank via PayShap
Outbound_rtp_paid (Priority: 2)
Given:
rtp_status(db) eqpresented- Debtor authorises and completes payment
Then:
- transition_state field:
rtp_statusfrom:presentedto:paid - emit_event event:
rtp.outbound.response_received
Result: Debtor paid the RTP — response delivered to partner via callback
Outbound_rtp_rejected (Priority: 3)
Given:
rtp_status(db) eqpresented- Debtor rejects the request to pay
Then:
- transition_state field:
rtp_statusfrom:presentedto:rejected - emit_event event:
rtp.outbound.response_received
Result: Debtor rejected the RTP — response delivered to partner via callback
Outbound_rtp_expired (Priority: 4)
Given:
rtp_status(db) eqpresentedexpiry_date_time(db) ltnow
Then:
- transition_state field:
rtp_statusfrom:presentedto:expired - emit_event event:
rtp.outbound.response_received
Result: RTP expired without a response from the debtor
Inbound_rtp_received (Priority: 5)
Given:
- Electrum delivers inbound RTP from another institution
uetr(input) existsdebtor_party_name(input) exists
Then:
- emit_event event:
rtp.inbound.received - call_service target:
partner_system.process_inbound_rtp— POST to partner’s /transactions/inbound/request-to-pay
Result: Inbound RTP delivered to partner for processing — partner responds via callback
Rtp_cancellation_success (Priority: 6)
Given:
rtp_status(db) eqpresented- Cancellation request submitted before payment
Then:
- transition_state field:
rtp_statusfrom:presentedto:cancelled - emit_event event:
rtp.cancellation.requested - emit_event event:
rtp.cancellation.response_received
Result: RTP cancellation succeeded — transaction marked as cancelled
Rtp_cancellation_already_paid (Priority: 7)
Given:
rtp_status(db) eqpaid- Cancellation request submitted after payment
Then:
- emit_event event:
rtp.cancellation.response_received
Result: Cancellation failed — debtor has already paid the RTP (status ALREADY_PAID)
Refund_initiated (Priority: 8)
Given:
- Partner system is authenticated
- A prior successful financial transaction exists for the given reference
uetr(input) exists
Then:
- call_service target:
electrum_chp.refund_initiation— POST /transactions/outbound/refund-initiation - emit_event event:
rtp.refund.initiated
Result: Refund initiated against a prior successful transaction — uses original amount and creditor as debtor if not specified
Refund_completed (Priority: 9)
Given:
- Refund initiation was submitted
- Refund successfully processed by the scheme
Then:
- emit_event event:
rtp.refund.response_received
Result: Refund completed — response delivered to partner via callback
Errors
| Code | Status | Message | Retry |
|---|---|---|---|
RTP_BAD_REQUEST | 400 | The request body is malformed or missing required fields | No |
RTP_UNAUTHORIZED | 401 | Authentication credentials are missing or invalid | No |
RTP_FORBIDDEN | 403 | The caller does not have permission to perform this operation | No |
RTP_NOT_FOUND | 404 | The requested RTP transaction was not found | No |
RTP_UNPROCESSABLE | 422 | The request is well-formed but contains semantic errors | No |
RTP_RATE_LIMITED | 429 | Too many requests — rate limit exceeded | No |
RTP_SERVER_ERROR | 500 | An unexpected error occurred on the server | No |
RTP_SERVICE_UNAVAILABLE | 500 | The service is temporarily unavailable | No |
Events
| Event | Description | Payload |
|---|---|---|
rtp.outbound.initiated | Partner initiated an outbound request-to-pay | uetr, end_to_end_identification, creditor_account_number, amount_value, amount_currency |
rtp.outbound.response_received | Response received for an outbound RTP (paid, rejected, expired) | uetr, rtp_status, amount_value, amount_currency |
rtp.inbound.received | Inbound RTP received from another institution via Electrum | uetr, end_to_end_identification, debtor_party_name, amount_value, amount_currency |
rtp.inbound.responded | Partner responded to an inbound RTP | uetr, rtp_status |
rtp.cancellation.requested | Cancellation requested for an RTP | uetr, cancellation_reason |
rtp.cancellation.response_received | Cancellation response received | uetr, rtp_status |
rtp.refund.initiated | Refund initiated for a prior successful transaction | uetr, refund_amount, amount_currency |
rtp.refund.response_received | Refund response received | uetr, refund_amount, amount_currency |
Related Blueprints
| Feature | Relationship | Reason |
|---|---|---|
| chp-inbound-payments | required | RTP payments result in inbound credit transfers |
| chp-outbound-payments | required | RTP responses delivered via outbound callbacks |
| chp-account-management | recommended | Proxy resolution for PayShap addressing |
AGI Readiness
Goals
Reliable Clearing House Request To Pay
Request-To-Pay (RTP) and refunds for clearing house payments — outbound/inbound RTP initiation, cancellation, and refund processing
Success Metrics:
| Metric | Target | Measurement |
|---|---|---|
| success_rate | >= 99.5% | Successful operations divided by total attempts |
| error_recovery_rate | >= 95% | Errors that auto-recover without manual intervention |
Constraints:
- availability (non-negotiable): Must degrade gracefully when dependencies are unavailable
- security (non-negotiable): Sensitive fields must be encrypted at rest and never logged in plaintext
Autonomy
Level: supervised
Escalation Triggers:
error_rate > 5
Verification
Invariants:
- sensitive fields are never logged in plaintext
- all data access is authenticated and authorized
- error messages never expose internal system details
- state transitions follow the defined state machine — no illegal transitions
Tradeoffs
| Prefer | Over | Reason |
|---|---|---|
| reliability | throughput | integration failures can cascade across systems |
Coordination
Protocol: orchestrated
Consumes:
| Capability | From | Fallback |
|---|---|---|
chp_inbound_payments | chp-inbound-payments | degrade |
chp_outbound_payments | chp-outbound-payments | degrade |
Safety
| Action | Permission | Cooldown | Max Auto |
|---|---|---|---|
| outbound_rtp_initiated | autonomous | - | - |
| outbound_rtp_paid | autonomous | - | - |
| outbound_rtp_rejected | supervised | - | - |
| outbound_rtp_expired | autonomous | - | - |
| inbound_rtp_received | autonomous | - | - |
| rtp_cancellation_success | supervised | - | - |
| rtp_cancellation_already_paid | supervised | - | - |
| refund_initiated | autonomous | - | - |
| refund_completed | autonomous | - | - |