Responding to an RFI
Each open RFI has two kinds of items: questions (free-text answers from the customer) and document requests (one or more files attached against a slot). Resolve every item and the case will close on the next review.
You can submit responses two ways:
- Per-item — one HTTP call per question answer or per document attach. Easiest for incremental UIs.
- Bulk — one HTTP call carrying every answer and attachment in a single batch. Atomic: if any item is invalid the whole batch is rejected (Error handling).
All endpoints require the standard AccountId and Authorization: Bearer <token> headers.
Answering a question
See the Answer an RFI question API reference for the full request/response schema.
curl -L -X POST 'https://sandbox-api.hubpay.io/v1/rfi/11111111-1111-1111-1111-111111111111/questions/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/answer' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'AccountId: 058a2d45-139c-436b-8d1f-eec265e0f418' \
-H 'Authorization: Bearer <token>' \
-d '{
"answer": "Funds originate from the sale of investment property completed in March 2026."
}'
The response is the updated Rfi, so you can use it to refresh your local copy in one round trip.
Re-submitting an answer to the same question replaces the previous answer. Once the case is closed or
cancelled, further answer attempts return 409 CONFLICT (RFI_NOT_OPEN).
Attaching documents to a document request
Document responses are a two-step flow:
- Upload the file bytes via the RFI-specific upload endpoint
POST /v1/rfi/{rfiId}/documents/upload. You get back adocumentUploadIdfor each file. - Attach one or more upload IDs to a specific document request on the RFI.
Step 1 — upload the file
curl -L -X POST 'https://sandbox-api.hubpay.io/v1/rfi/11111111-1111-1111-1111-111111111111/documents/upload' \
-H 'Content-Type: multipart/form-data' \
-H 'Accept: application/json' \
-H 'AccountId: 058a2d45-139c-436b-8d1f-eec265e0f418' \
-H 'Authorization: Bearer <token>' \
-F 'file=@"/path/to/bank-statement.pdf"'
The response carries the id of the upload — this is the documentUploadId you feed into the attach call
below.
Step 2 — attach the upload to the document request
See the Attach documents API reference for the full request/response schema.
curl -L -X POST 'https://sandbox-api.hubpay.io/v1/rfi/11111111-1111-1111-1111-111111111111/document-requests/cccccccc-cccc-cccc-cccc-cccccccccccc/attach' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'AccountId: 058a2d45-139c-436b-8d1f-eec265e0f418' \
-H 'Authorization: Bearer <token>' \
-d '{
"documentUploadIds": [
"d1d1d1d1-d1d1-d1d1-d1d1-d1d1d1d1d1d1"
]
}'
Multi-file requirements (e.g. front + back of an ID) can be satisfied by passing multiple upload IDs in a single attach call. They merge into a single document, which is what gets reviewed and what subsequent webhooks reference.
The attach response is the updated Rfi. The documentRequests[] entry you just satisfied now has a
non-null documentId — persist this, see below.
Critical — persist documentId for webhook correlation
v1.rfi.document.* webhooks identify the document by the
attached document's UUID, not the upload UUID you got from the upload endpoint. If you want to route an
incoming v1.rfi.document.approved (or .declined / .validation_warnings) back to the originating RFI,
you must persist a documentId → rfiId mapping.
documentUploadId → rfiIdA common — and silently broken — pattern is to persist the upload ID from step 1. The webhook never carries
that ID. Persist the documentId from the attach response (step 2) instead.
Pattern:
On attach response, for each entry in Rfi.documentRequests[]:
if entry.documentId is not null:
map[entry.documentId] = Rfi.id
On webhook arrival, look up event.data.documentId in the map to find the parent rfiId.
Each entry also exposes documentUploads[] — the same files surfaced as a list — so if you prefer to walk
the file list, Rfi.documentRequests[].documentUploads[].id resolves to the same UUID as documentId.
Either path populates the same map.
Submitting multiple responses in bulk
If your UI lets the customer answer everything at once and submit, the bulk endpoint avoids N round-trips. See the Submit RFI responses in bulk API reference for the full request/response schema.
curl -L -X POST 'https://sandbox-api.hubpay.io/v1/rfi/11111111-1111-1111-1111-111111111111/responses' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'AccountId: 058a2d45-139c-436b-8d1f-eec265e0f418' \
-H 'Authorization: Bearer <token>' \
-d '{
"answers": [
{
"questionId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"answer": "Funds originate from the sale of investment property completed in March 2026."
},
{
"questionId": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
"answer": "Yes — please find the latest audit report attached."
}
],
"attachments": [
{
"documentRequestId": "cccccccc-cccc-cccc-cccc-cccccccccccc",
"documentUploadIds": [
"d1d1d1d1-d1d1-d1d1-d1d1-d1d1d1d1d1d1"
]
}
]
}'
The bulk call is atomic — if any answer or attachment is invalid, the entire batch is rejected and no
state changes. The response, on success, is the updated Rfi. Walk documentRequests[] and persist
documentId → rfiId for the new entries exactly as you would for a per-item attach.
Replace semantics
| Action | Behaviour |
|---|---|
| Re-answering a question | Replaces the previous answer in place. |
| Re-attaching to a satisfied document slot | Replaces the previously attached document. The earlier document is retired and will not be reviewed. |
Submitting once an item is APPROVED | Returns 409 CONFLICT (RFI_ITEM_ALREADY_APPROVED). Approved items are locked. See error handling. |
| Submitting once the case is closed/cancelled | Returns 409 CONFLICT (RFI_NOT_OPEN). See error handling. |
Refer to the API Reference for the full request/response schemas.