Requests for information
When a verification needs something the submitted profile didn't provide, Seismic compliance raises a request for information (RFI) against it: a short list of specific asks, emailed to the org's compliance address and readable here.
Every item is answered through the answers endpoints, with a written answer, a document already uploaded for the subject, or both. Each answer closes only the item it answers, and a request reads as answered once every one of its items has an answer; the answers are then reviewed, and any profile change they support is made as part of that review rather than by the answer itself.
Endpoints, one pair per subject:
GET /customers/:id/kyc/rfis— one customer's open requests.POST /customers/:id/kyc/rfis/answers— answer a customer's items.GET /org/kyb/rfis— the org's own KYB requests.POST /org/kyb/rfis/answers— answer the org's own items.
The reads return the same body, and both are the same data the hosted verification form shows in its "Pending RFIs" step.
GET /customers/:id/kyc/rfis
Open requests on one customer's verification — KYC for individuals, KYB for businesses.
Response 200
{
"rfis": [
{
"id": "3f1c…",
"sent_at": "2026-09-10T14:02:11.000Z",
"items": [
{
"item_index": 0,
"text": "Please provide the residential street address.",
"kind": "other"
},
{
"item_index": 1,
"text": "Please upload a bank statement from the last 3 months.",
"kind": "document"
},
{
"item_index": 2,
"text": "Please upload a diagram showing the entity's complete transaction flow.",
"kind": "document",
"document_kind": "flow_of_funds"
}
]
}
],
"answered": []
}| Field | Type | Notes |
|---|---|---|
rfis | array | Open requests, oldest first. Empty when nothing is outstanding. |
answered | array | Requests already answered, newest first — a read-only history of the questions and the answers given. See Answered history. |
id | string | Request id. Stable for the life of the request. |
sent_at | string | Optional. ISO-8601 timestamp of when the request went out. Absent on a request that has been raised but not yet sent. |
items | array | The individual asks. Always at least one. |
Each entry in items:
| Field | Type | Notes |
|---|---|---|
item_index | number | Position of the item within the request. Quote it back when answering. |
text | string | The request, written for the person completing the form. Free-form — render it, don't parse it. |
kind | enum | document | other. A hint about the expected answer: a document upload, or a written answer. Every item accepts either or both. |
document_kind | string | Optional. The document slot an upload answering this item fills, as a document kind slug. Upload the file under this kind so it lands in place. Present only when the request names a slot. |
person_name | string | Optional. The associated person the request concerns, when it concerns one. |
Errors
| Code | Status | Meaning |
|---|---|---|
Unauthorized | 401 | Missing or invalid API key. |
CustomerNotFound | 404 | No such customer in this org. |
GET /org/kyb/rfis
Open requests on the org's own KYB, the counterpart of the per-customer read. Same response body.
An org that has not saved a KYB profile yet has no verification to hold a request against, so it returns an empty rfis array rather than a 404 — a sweep doesn't have to special-case the org that hasn't started.
curl https://api.seismic.io/v0/org/kyb/rfis \
-H "Authorization: Bearer $SEISMIC_API_KEY"Answered history
answered is the read-only record the hosted verification form shows as RFI Log: for every request with answered items, each question as it was asked and the latest answer on record. It stays readable at every status, including after the verification is decided, and is never editable — a new answer supersedes rather than rewrites.
{
"rfis": [],
"answered": [
{
"id": "3f1c…",
"sent_at": "2026-09-10T14:02:11.000Z",
"items": [
{
"question": "Please explain the purpose of the incoming transfers.",
"answer": "Monthly retainer payments under an ongoing design contract.",
"answered_at": "2026-09-12T09:15:40.000Z"
},
{
"question": "Please upload a bank statement from the last 3 months.",
"document_id": "9a4e…",
"document_filename": "statement-2026-08.pdf",
"answered_at": "2026-09-12T09:15:40.000Z"
}
]
}
]
}Each entry in items carries answer, document_id, or both, depending on how the item was answered. document_id is the document as it is on record now — the upload itself, or the document it was filed as during review — and opens through the documents endpoints. Once no copy remains, document_id is omitted and only document_filename is kept.
Answering items
Answers are recorded in place, against the request and item they answer:
POST /customers/:id/kyc/rfis/answers— one customer's requests.POST /org/kyb/rfis/answers— the org's own KYB requests.
A document answer references a document already uploaded for the same subject through the documents endpoints. Each answer names the request and the item it answers:
{
"answers": [
{
"rfi_id": "3f1c…",
"item_index": 0,
"answer": "The March deposit was the proceeds of a company vehicle sale."
},
{
"rfi_id": "3f1c…",
"item_index": 1,
"document_id": "9a4e…"
}
]
}| Field | Type | Notes |
|---|---|---|
answers | array | At least one answer. |
rfi_id | string | The open request's id, from the read above. |
item_index | number | The answered item's item_index, from the read above. |
answer | string | Optional when document_id is given. The written answer, at most 10,000 characters. |
document_id | string | Optional when answer is given. Id of a document uploaded for this subject that answers the item. |
Response 200
{
"recorded": 1,
"supplemental_entries": [
{
"id": "5b5b5b5b-5b5b-4b5b-8b5b-5b5b5b5b5b5b",
"kind": "document",
"description": "Please provide a bank statement covering the last three months.",
"document_id": "9a4e4e4e-4e4e-4e4e-8e4e-4e4e4e4e4e4e"
}
]
}recorded counts the answers newly recorded; an answer identical to the latest recorded one for the same item is acknowledged without being recorded again.
A document supplied in an answer is carried with the profile from then on. One uploaded under a structured kind, such as proof_of_address, has already replaced the profile's document of that kind. One uploaded under the catch-all other kind has no slot of its own, so it is placed during review: the reviewer either moves it into the slot it belongs in, replacing the document there, or files it in the profile's supplemental section under a title saying what the document is. supplemental_entries lists any entries filed by this call itself, which happens only when no review will place the document: each carries the item it answers as its description, so the entry passes the section's submit rules as filed. A document the section already attaches is not filed again. A later profile PUT replaces the section in full, so include filed entries in it to keep them.
Each answer closes only the item it answers. Items left out of a call stay in the open list, and pending_rfi stays true, until they are answered too. Written answers are kept verbatim on the verification's record for compliance review, and any profile change they support is made during that review.
Errors
| Code | Status | Meaning |
|---|---|---|
InvalidRequest | 400 | Empty batch, an answer with neither answer nor document_id, blank or oversized text, or an unknown item_index. |
Unauthorized | 401 | Missing or invalid API key. |
NotFound | 404 | No such request on this subject, or no such document. |
| — | 409 | The request is no longer open. |
When requests are visible
A request stays readable while the verification can still act on it, and disappears once it can't:
- It is answered. Submitting the profile again — or recording answers through the answers endpoint — answers every open request on it. The request leaves this list on the next read, whether or not the answer actually covered what was asked.
- The verification is decided. An approval, a rejection, or a hold supersedes every open request — a decided verification has nothing outstanding.
Relationship to pending_rfi
GET /customers returns a pending_rfi boolean per row, and GET /customers/:id/kyc/state returns one for a single customer. It is exactly rfis being non-empty, derived from the same rule above — use it to find which customers to read requests for, then call this endpoint for the detail.
Filter the list on it directly to sweep for work:
curl "https://api.seismic.io/v0/customers?pending_rfi=true&limit=100" \
-H "Authorization: Bearer $SEISMIC_API_KEY"Polling
There is no webhook for a raised request today. pending_rfi on the filtered customer list is the intended signal: one call per sweep returns only the customers with something outstanding, so the cost doesn't grow with the size of your customer base.
