Building Your RFI Form
This guide covers how to render each RFI question type and walks through a complete end-to-end integration example using sample webhook payloads and API calls.
For the conceptual overview of the flow, see RFI v5 Flow: Overview. For the full API specification, see the RFI API Reference.
Rendering questions from the payload
Build a generic renderer that switches on the field types from the RFI payload and produces the appropriate input components. Do not maintain a separate question schema — the payload is the single source of truth.
The Fetch RFI API response includes two arrays:
mandatoryFieldTypes— response types the client must provide.optionalFieldTypes— response types the client may additionally include (for example, a supporting comment or file attachment).
An RFI may accept multiple response types simultaneously. Render a component for each type listed in mandatoryFieldTypes, and offer optional inputs for each type in optionalFieldTypes. Ignore unknown type values gracefully so your integration does not break when Nium adds new types.
Use the label field as the heading or title for the RFI — it is a short, standardised string (for example, "Source of Funds" or "Date of Birth") that gives the end-user an at-a-glance cue before they read the full question. Use the query field as the question body displayed below the heading. Use the comment field (when present) as supplementary guidance — for example, a tooltip or helper text. Use metadata.text for input format hints.
TEXT
Render a multi-line text area.
query: "Please provide the source of funds for this transaction."
comment: "Include the originating account name and institution."
Response field: response.text (string, max 2000 characters)
CHOICE
Render a single-select dropdown. Populate options using the Nium Constants API with the metadata.enumCategory value.
query: "Select the purpose of this transfer."
metadata.enumCategory: "PURPOSE_CODE"
Response field: response.choice (single enum value from the options list)
MULTI_CHOICE
Render a multi-select list. Populate options using the Nium Constants API with metadata.enumCategory.
query: "Select all applicable business activities."
metadata.enumCategory: "BUSINESS_ACTIVITY"
Response field: response.multiChoice (array of enum values)
ADDRESS
Render six separate input fields.
| Field | Label | Notes |
|---|---|---|
addressLine1 | Address Line 1 | Max 255 characters |
addressLine2 | Address Line 2 | Optional, max 255 characters |
city | City / Suburb | Max 100 characters |
state | State / Province | Max 100 characters |
country | Country | 2-character ISO 3166-1 alpha-2 code (e.g. US, GB) |
postcode | Postcode | Alphanumeric, 3–10 characters |
Response field: response.address (object)
FILE_ATTACHMENT
Render a file upload control. Before submitting the RFI response, upload each file using the Create File API and collect the returned file IDs.
response.fileAttachment accepts file IDs, not raw file data. Always upload files first and reference the IDs.
Response field: response.fileAttachment (array of UUID strings)
IDENTITY_DOCUMENT
Render a document upload flow. Supported document types: Passport, Driver's Licence, National ID.
Collect:
- Front of document (required)
- Back of document (optional)
- Document type (required)
- Document number, expiry date, and issuance country (optional but recommended)
Upload each image using the Create File API and reference the returned file IDs. The issuance country must be a 2-character ISO 3166-1 alpha-2 code. Document number and reference number are each max 100 characters.
Response field: response.identityDocument (array of objects)
CONFIRMATION
Render a checkbox or toggle.
query: "I confirm that the information provided is accurate and complete."
Response field: response.confirmation (boolean)
DATE
Render a date picker. The expected format is YYYY-MM-DD.
query: "Please provide the document expiry date."
metadata.text: "YYYY-MM-DD"
Response field: response.date (string, YYYY-MM-DD)
URL
Display the URL as a link or embedded button. The end-user completes the linked flow or downloads the template, then may need to upload a completed document as a file attachment in a follow-up RFI.
query: "Please complete the hosted KYC form at the link below."
url: "https://hosted-kyc.nium.com/session/abc123"
No response field required — the action is completed externally.
Handling optional comment and attachment
Every question type supports an optional top-level comment field in the submit request, regardless of the primary field types. Render this as an optional text area labelled "Additional context (optional)" below each question.
The comment field is separate from response — it sits at the top level of each request object alongside rfiId and response.
Handling unanswered RFIs
When an end-user cannot or will not provide the requested information, set cannotRespondReason inside the response object:
[
{
"rfiId": "rfi_abc123",
"response": {
"cannotRespondReason": "END_USER_UNRESPONSIVE"
},
"comment": "Customer did not respond after three contact attempts."
}
]
Valid values for cannotRespondReason: END_USER_UNRESPONSIVE, END_USER_DEACTIVATED, TRANSACTION_CANCELLED.
Using cannotRespondReason is treated as a request to cancel the associated onboarding application, transaction, or other entity. Use it only when the process should be abandoned. You can combine a partial response with cannotRespondReason for multi-type RFIs where some fields can be provided but not others.
End-to-end example
This example walks through a complete RFI cycle for a transaction where Nium requests the source of funds as free text.
Step 1 — Receive the webhook
Nium fires rfi.requested to your registered endpoint:
{
"rfiId": "rfi_7f3a9c12",
"rfiEntityReferenceId": "txn_00099182",
"externalReferenceId": "ext_ref_99182",
"rfiEntity": "TRANSACTION",
"entityType": "CUSTOMER",
"status": "RFI_REQUESTED",
"query": "Please provide the source of funds for this transaction.",
"comment": "Include the originating account name and financial institution.",
"url": null,
"label": "Source of Funds",
"type": "TEXT",
"metadata": {
"text": null,
"enumCategory": null
},
"rfiRaisedAt": "2026-09-04T08:00:00.000Z",
"rfiRespondedAt": null
}
Persist rfiId (rfi_7f3a9c12) and rfiEntityReferenceId (txn_00099182).
Step 2 — Render the question
Your renderer finds TEXT in mandatoryFieldTypes (or type in the webhook) and renders:
- Label: "Source of Funds"
- Question: "Please provide the source of funds for this transaction."
- Helper text: "Include the originating account name and financial institution."
- Input: Multi-line text area
- Optional: Comment field, supporting document upload
Step 3 — Collect the end-user response
The end-user types:
"Salary from Acme Corp, HSBC UK account ending 4821."
They also add a comment: "Payslip attached for reference." and upload a payslip (file ID returned from Create File API: file_c9d2e3f4).
Step 4 — Submit the response
POST /api/v5/client/{clientHashId}/rfi/response
[
{
"rfiId": "rfi_7f3a9c12",
"comment": "Payslip attached for reference.",
"response": {
"text": "Salary from Acme Corp, HSBC UK account ending 4821.",
"fileAttachment": ["file_c9d2e3f4"]
}
}
]
Note that comment is top-level on the request object, not inside response.
Step 5 — Receive confirmation
Nium fires rfi.responded and the API returns:
{
"total": 1,
"success": 1,
"failed": 0,
"results": [
{
"rfiId": "rfi_7f3a9c12",
"status": "RFI_RESPONDED",
"respondedAt": "2026-09-04T08:15:43.000Z",
"message": "RFI responded successfully.",
"errors": []
}
]
}
Update your UI to reflect that the RFI has been responded to and no further action is needed.
UI best practices
Drive the UI from state, not from the last webhook. On any user action, re-fetch the RFI and re-render based on the returned status. This avoids stale UI when webhooks arrive out of order or are missed.
Render questions dynamically. A generic renderer that iterates over mandatoryFieldTypes and optionalFieldTypes from the Fetch RFI API handles every current and future question type without code changes when Nium adds new types — provided the renderer ignores unknown types gracefully.