Respond to RFIs
Use this component to show a compliance Request for Information (RFI) form in your platform. The form is tied to one payout transaction, or to a customer onboarding RFI. It supports customer mode and agent-on-behalf mode.
An RFI appears after a payout, when Nium's compliance engine flags a transaction for more information. The SDK renders the form in a secure iFrame. Nium's backend decides the questions and required fields, not the SDK.
This component:
- Binds each transaction RFI form to one
transactionId. If the transaction has no open RFI, the hosted form shows an error to the user. SetrfiType: 'customer_onboarding'to open a KYC/KYB onboarding RFI instead. - Renders the questions, field types, and validations from Nium's backend schema. The compliance engine generates them at runtime, so you can't preview or mock them offline.
- Lets users attach files, such as ID scans and invoices, to the response. Uploads go through Nium's backend.
- Supports agent mode, so a bank operator can complete the form for a customer.
Live preview
This is the component running against the Nium sandbox with test data. Answer the questions and upload a document to try the RFI form.
Before you begin
- Complete the Quick start to set up PKCE authorization. Use the scope
['rfi-form']when your server requests the authorization code. The same scope covers transaction and customer onboarding RFIs. - For a transaction RFI, have these three values for the flagged transaction:
customerHashId,walletHashId, andrfiAuthCode. Store them when you receive thetransaction.rfi_requiredwebhook. See The rfiAuthCode field. - Make sure the transaction has an open RFI. The SDK can't render a form without a live compliance request. To test in QA, create a transaction that triggers an RFI, or ask your Nium implementation partner for a test
systemReferenceNumber. - Set
customizations.minHeightto 600 or more. After the RFI questions load, the iFrame no longer auto-resizes, sominHeightsets its visible height.
Use rfi-form, not transaction-history-form. The second one is the scope for a different element, and using it is a common mistake.
Quick example
After you call init(), create the rfiForm element with all three required fields, mount it, and destroy it when the user closes your RFI panel.
- JavaScript
- React
<div id="rfi-root"></div>
const { createElement } = await import('@nium/nium-sdk')
const element = await createElement('rfiForm', {
customerHashId, // required
walletHashId, // required
rfiAuthCode, // required: same value as systemReferenceNumber
customizations: { minHeight: 600 },
})
element.on('ready', () => hideLoadingSpinner())
element.on('error', (e) => console.error(e.error.code, e.error.message))
// No submit or cancel events reach your page. See Events.
element.mount('#rfi-root')
// Your own close control destroys the element and checks completion server-side
closeButton.addEventListener('click', () => {
element.destroy() // an RFI accepts one submission
refreshRfiStatus()
})
import { useEffect } from 'react'
interface Props {
customerHashId: string
walletHashId: string
rfiAuthCode: string // = transactionId = systemReferenceNumber
onError: (message: string) => void
}
export function RfiFormWidget({ customerHashId, walletHashId, rfiAuthCode, onError }: Props) {
useEffect(() => {
let element: { destroy(): void } | undefined
let cancelled = false
;(async () => {
const { createElement } = await import('@nium/nium-sdk')
element = await createElement('rfiForm', {
customerHashId,
walletHashId,
rfiAuthCode,
customizations: { minHeight: 600 },
})
if (cancelled) return element.destroy()
// Only ready and error reach your page. Unmounting this widget destroys the iFrame.
element.on('error', (e) => onError(e.error.message))
element.mount('#rfi-root')
})()
return () => {
cancelled = true
element?.destroy()
}
}, [rfiAuthCode]) // re-create if the rfiAuthCode changes
return <div id="rfi-root" style={{ minHeight: 600 }} />
}
Get a fresh authorization code right before each init() call. For a transaction RFI, the SDK checks customerHashId, rfiAuthCode, and walletHashId before it creates the element. If any value is missing or empty, createElement() rejects with MISSING_REQUIRED_OPTION.
The rfiAuthCode field
rfiAuthCode is one value that has different names in different places.
| Name | Where it appears |
|---|---|
rfiAuthCode | Field name in NiumRfiFormOptions (SDK parameter). |
transactionId | Field name in Nium API responses and your internal systems. |
systemReferenceNumber | Field name in Nium webhook payloads, for example the transaction.flagged webhook. |
authCode | Field name in the RFI session that the hosted form creates (internal). |
When your webhook handler receives a transaction.rfi_required event, pass the systemReferenceNumber from the payload as rfiAuthCode. Store it with the customerHashId and walletHashId. You need all three to load the form.
export async function POST(req: Request) {
const payload = await req.json()
if (payload.event === 'transaction.rfi_required') {
await db.rfiRequests.create({
transactionId: payload.systemReferenceNumber, // save as rfiAuthCode
customerHashId: payload.customerHashId,
walletHashId: payload.walletHashId,
status: 'pending',
})
await notifyCustomer(payload.customerHashId, 'Action required: information needed for your transfer')
}
return Response.json({ ok: true })
}
// Later, when the customer selects "Provide information", load the stored record
// and pass its three values to createElement('rfiForm', ...).
Options
createElement('rfiForm', options) accepts these options (type NiumRfiFormOptions). For a transaction RFI, three fields are required, and createElement() rejects if any is missing.
| Option | Type | Description |
|---|---|---|
customerHashId | string | Required. Nium customer ID of the transaction owner. Must match the customer on the flagged transaction. |
rfiType | 'transaction' | 'customer_onboarding' | Which RFI to open. Defaults to 'transaction'. 'customer_onboarding' opens a KYC/KYB onboarding RFI and needs only customerHashId. Don't pass rfiAuthCode or walletHashId with it. An invalid value throws INVALID_RFI_TYPE. |
rfiAuthCode | string | Required for transaction RFIs. The RT number of the flagged transaction. Same as systemReferenceNumber and transactionId. See The rfiAuthCode field. |
walletHashId | string | Required for transaction RFIs. Nium wallet of the flagged transaction. Available in the webhook payload. |
onBehalf | boolean | true for agent or bank operator mode. Requires email. See Agent mode. |
email | string | Agent's email address. Required when onBehalf: true. Identifies the operator who completed the form. |
theme | NiumTheme | Visual overrides. Has no effect on this component. See Theming. |
customizations | { minHeight?: number } | Sets the iFrame's visible height. Pass minHeight: 600 or more so multi-question forms, upload areas, and validation messages aren't truncated. The form scrolls inside the iFrame beyond that height. |
Agent mode
Banks and financial institutions can complete an RFI for their customers. Set onBehalf: true and the agent's email to turn on this mode.
- Standard mode (default). The end customer completes the RFI.
onBehalfisfalseor omitted. No extra fields are needed. - Agent mode. A bank operator completes the form. Nium records the operator's email in the audit trail and shows a modified form that indicates the agent context.
await createElement('rfiForm', {
customerHashId,
walletHashId,
rfiAuthCode,
onBehalf: true,
email: 'agent@yourbank.com', // recorded in the audit trail
customizations: { minHeight: 600 },
})
Events
Call element.on(eventName, handler) after createElement(), before or after mount(). Each call returns a function that removes that listener. Store it and call it on teardown.
ready: The hosted page loaded and connected to the SDK, before the RFI questions appear. The iFrame shows its own loading spinner until then. No payload.error: The SDK or iFrame failed to load, the auth code exchange failed, a parameter is missing, or the RFI session can't be created (SESSION_CREATION_FAILED). Errors inside the RFI form itself, for example no open RFI on the transaction, appear to the user but aren't relayed to your page. Always attach this handler. Payload:{ error: { message: string, code: string } }.
After loading, the iFrame switches to Nium's RFI app on a different origin. The SDK accepts messages only from the hosted-form origin. So submit, cancel, and resize never reach your page, and element.submit() isn't supported. Detect a completed RFI on your server, through the transaction status or a webhook. Give users your own control to close the RFI panel.
Common errors and fixes
| Error or symptom | Cause | Fix |
|---|---|---|
createElement() missing required option: rfiAuthCode | rfiAuthCode is an empty string or undefined. | Check for empty values in your UI before you call createElement(). |
createElement() missing required option: walletHashId | walletHashId is an empty string or undefined. | Store walletHashId with systemReferenceNumber in your webhook handler. |
Form loads but shows no RFI found | The transaction has no open RFI, or the RFI was already submitted. | Check the transaction status through the Nium API. An RFI accepts one submission. |
| Form is truncated or cut off | minHeight isn't set, or the container is too small. | Add customizations: { minHeight: 600 } or more. The form scrolls inside the iFrame past that height. |
| Wrong scope error | You passed transaction-history-form instead of rfi-form. | Check the literal string in your auth call: "scopes": ["rfi-form"]. |
| Auth code expired or invalid | The auth code was already used or is older than its expiresInSecs window, typically 300 seconds. | Get a new auth code right before each init() call. |
Clean up
An RFI accepts one submission. There's no submit event to hook. Call element.destroy() from your own close control, or when your backend reports the RFI as answered, then show your confirmation. Don't remount the same instance. Don't load a new RFI on a transaction that's already answered.
element.destroy(): Removes the iFrame and all listeners permanently. Create a new element to show another form.element.unmount(): Removes the iFrame and keeps the instance. Callmount()to show it again. It's rarely used for RFI because the form accepts one submission.
Theming
NiumTheme isn't supported on this component. NiumRfiFormOptions accepts the theme option without an error, but it has no visual effect. This component uses a separate compliance rendering layer that doesn't expose theming parameters.
For details, see Theme customization.
Next steps
- Track payouts: Show the status of the payout after the user responds.
- Send payouts: Let users start payouts.