customidinput
An ID number input with live external verification. Validates against NIDA/NIN registries and auto-populates downstream fields from the API response.
An ID number input with live external verification. Validates against NIDA/NIN registries and auto-populates downstream fields from the API response.
Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.
When to use
- Capturing a Rwandan National ID (NID), Foreigner ID, Refugee ID, Child ID, or NIN with live registry verification
- Any document ID that must be validated against an external registry
When NOT to use
Instead of customidinput, use… | For… |
|---|---|
input | Plain text reference numbers with no external verification |
customtininput | TIN (Tax Identification Number) — never use customidinput for TINs |
customphonenumber | Phone numbers |
ID type reference
Every customidinput requires idType. For idType: "NID", nidType is also required and controls which NID prefix is accepted.
idType | nidType | idLength | endpointCode | usePrefetch |
|---|---|---|---|---|
NID | NATIONAL_ID | 16 | NIDAGETIDINFO | true |
NID | FOREIGNER_ID | 16 | NIDAGETIDINFO | false |
NID | REFUGEE_ID | 16 | NIDAGETIDINFO | false |
CHILD_ID | — | 8 | NIDAGETCHILDINFO | false |
NIN | — | 10 | not required | false |
Props
| Prop | Type | Required? | Description |
|---|---|---|---|
label | string | required | Visible label. Must be unique across the form. |
required | boolean | required | Always false. The expression controls the runtime value. |
defaultRequired | boolean | required | true for required fields, false for optional ones. |
idType | 'NID' | 'CHILD_ID' | 'NIN' | required | Document type being collected. |
nidType | 'NATIONAL_ID' | 'FOREIGNER_ID' | 'REFUGEE_ID' | required when idType is 'NID' | NID subtype. Controls accepted prefix: NATIONAL_ID starts with 1, REFUGEE_ID with 2, FOREIGNER_ID with 3. |
idLength | number | required | Expected digit count. 16 for NID, 8 for CHILD_ID, 10 for NIN. |
useBaseUrl | boolean | required | Always true — prepends the API gateway base URL. |
endpointCode | string | required for NID and CHILD_ID | Integration endpoint code. Not required for NIN. |
usePrefetch | boolean | optional | true auto-fetches the citizen's registered ID from their profile when the field becomes visible. Only works for NATIONAL_ID — always false for FOREIGNER_ID, REFUGEE_ID, CHILD_ID. |
populates | array | optional | { valueKey, targetKey } pairs mapping API response keys to form field keys. Must be an array, not an object. |
idConditions | object | optional | Age constraint applied after fetch. See below. |
placeholder | string | optional | Ghost text shown inside the field. |
readonly | boolean | optional | Renders the input non-editable. |
hint | string | optional | Help text shown below the field. |
tooltip | string | optional | Tooltip shown on hover. |
hideField | boolean | optional | Hides field and excludes its value. Toggle via expressions["props.hideField"]. |
idConditions.ageLimit
Enforces a minimum and/or maximum age after the ID lookup. key must be a valueKey returned by the endpoint (usually "dateOfBirth"). Age bounds are in years.
"idConditions": {
"ageLimit": {
"key": "dateOfBirth",
"minimumAge": 18,
"maximumAge": 64
}
}When used, add "minimumAge" and/or "maximumAge" to validators.validation and include their messages in validation.messages.
API response keys (populates.valueKey)
NIDAGETIDINFO (idType: "NID")
valueKey | Description |
|---|---|
surname | Family name |
forename | Given name(s) |
fatherName | Father's name |
motherName | Mother's name |
dateOfBirth | Date of birth |
sex | Gender |
nationality | Nationality |
countryOfBirth | Country of birth |
placeOfBirth | Place of birth |
civilStatus | Civil / marital status |
citizenStatus | Liveness — "ALIVE", "DEAD", or "DECEASED" |
NIDAGETCHILDINFO (idType: "CHILD_ID")
valueKey | Description |
|---|---|
surname | Family name |
forename | Given name(s) |
fatherName | Father's name |
motherName | Mother's name |
dateOfBirth | Date of birth |
sex | Gender |
countryOfBirth | Country of birth |
placeOfBirth | Place of birth |
civilStatus | Civil / marital status |
Validation messages
| Key | When it fires |
|---|---|
required | Field is empty and required |
invalidId | ID does not pass format check |
invalidInput | ID does not match expected idLength |
invalidNidInput | ID is not a valid NID for the given nidType |
verificationFailed | NIDA API call failed (network / server error) |
verificationInvalid | ID not found in NIDA records |
verificationRequired | User has not confirmed consent |
endpointCodeNotConfigured | endpointCode is missing — field locks as read-only |
Examples
National ID with population and liveness check
{
"key": "APPLICANT_NID",
"type": "customidinput",
"props": {
"label": "National ID Number",
"idType": "NID",
"nidType": "NATIONAL_ID",
"idLength": 16,
"useBaseUrl": true,
"endpointCode": "NIDAGETIDINFO",
"usePrefetch": true,
"required": false,
"defaultRequired": true,
"placeholder": "Enter your National ID number",
"populates": [
{ "valueKey": "surname", "targetKey": "LAST_NAME" },
{ "valueKey": "forename", "targetKey": "FIRST_NAME" },
{ "valueKey": "dateOfBirth", "targetKey": "DATE_OF_BIRTH" },
{ "valueKey": "sex", "targetKey": "GENDER" }
]
},
"expressions": {
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
},
"validation": {
"messages": {
"required": "This field is required.",
"invalidId": "The ID number should be a valid Rwandan ID.",
"invalidInput": "National ID must be 16 digits.",
"invalidNidInput": "Invalid National ID number.",
"verificationFailed": "ID verification failed. Please try again.",
"verificationInvalid": "Details don't match NIDA records. Please check the ID owner's info and try again.",
"verificationRequired": "Please verify that you have consent from the owner of the ID.",
"applicantMustBeAlive": "The national ID should not belong to a deceased person.",
"applicantMustBeAlive1": "The national ID should not belong to a deceased person.",
"endpointCodeNotConfigured": "Missing endpoint code in ID Component."
}
},
"validators": {
"validation": [
{
"name": "genericUtilValidator",
"options": {
"prop1": ["options", "formState", "FETCHED_DATA", "APPLICANT_NID", "citizenStatus"],
"prop2": "DEAD",
"comparator": "!==",
"validationName": "applicantMustBeAlive"
}
},
{
"name": "genericUtilValidator",
"options": {
"prop1": ["options", "formState", "FETCHED_DATA", "APPLICANT_NID", "citizenStatus"],
"prop2": "DECEASED",
"comparator": "!==",
"validationName": "applicantMustBeAlive1"
}
}
]
}
}Child ID with age validation
{
"key": "CHILD_ID_NUMBER",
"type": "customidinput",
"props": {
"label": "Child ID",
"idType": "CHILD_ID",
"idLength": 8,
"useBaseUrl": true,
"endpointCode": "NIDAGETCHILDINFO",
"usePrefetch": false,
"required": false,
"defaultRequired": true,
"idConditions": {
"ageLimit": {
"key": "dateOfBirth",
"maximumAge": 17
}
},
"populates": [
{ "valueKey": "surname", "targetKey": "CHILD_LAST_NAME" },
{ "valueKey": "forename", "targetKey": "CHILD_FIRST_NAME" }
]
},
"expressions": {
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
},
"validation": {
"messages": {
"required": "This field is required.",
"invalidId": "Please enter a valid Child ID.",
"invalidInput": "Child ID must be 8 digits.",
"verificationFailed": "ID verification failed. Please try again.",
"verificationInvalid": "Details don't match any record.",
"verificationRequired": "Please verify that you have consent from the ID owner.",
"endpointCodeNotConfigured": "Missing endpoint code in ID Component.",
"maximumAge": "The child must be under 18 years old."
}
},
"validators": {
"validation": ["maximumAge"]
}
}Auto-populated readonly field pattern
Fields that receive values from populates should be hidden until the value is fetched. The props.hideField expression combines the trigger condition AND the presence of the populated value.
{
"key": "FIRST_NAME",
"type": "input",
"props": {
"label": "First Name",
"readonly": true,
"required": false,
"hideField": true,
"defaultRequired": true
},
"expressions": {
"props.hideField": "!(model?.APPLICANT_NID && model?.FIRST_NAME)",
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
},
"validation": {
"messages": {
"required": "This field is required."
}
}
}The expression !(model?.NID_FIELD && model?.THIS_FIELD) shows the readonly field only when both the ID lookup has run and this field has a value.
Common mistakes
- Omitting
endpointCodefor NID or CHILD_ID — the component locks as read-only at runtime. - Wrong
idLength— NID is16, CHILD_ID is8, NIN is10. - Setting
populatesas an object instead of an array — throws'populates should be an array'at runtime. - Using wrong
valueKeynames — the real keys aresurnameandforename(notsurnames,foreName). Mismatched keys silently fail. - Setting
usePrefetch: truefor FOREIGNER_ID or REFUGEE_ID — prefetch only works for NATIONAL_ID. - Setting
nidTypewhenidTypeis not"NID"—nidTypeis meaningless for CHILD_ID and NIN. - Omitting
useBaseUrl: true— the identity endpoint will not resolve through the API gateway. - Using
customidinputfor TIN numbers — usecustomtininputinstead.
Checklist
-
keyisUPPER_SNAKE_CASEand unique across the entire form -
props.labelis present, unique, and correctly cased - Field is nested at the correct depth:
sections > formly-group > block > customidinput -
required: falseis set (nevertrue) -
defaultRequiredis set -
expressions["props.required"]is present with the exact required expression -
idType,idLength, anduseBaseUrl: trueare set -
nidTypeis set whenidTypeis"NID" -
endpointCodeis set for NID and CHILD_ID -
usePrefetchisfalsefor FOREIGNER_ID, REFUGEE_ID, CHILD_ID, and NIN - All validation messages are present (at minimum the 7 core ones)
-
populatesis an array, and alltargetKeyvalues match real field keys in the form - Populated target fields are
readonly: truewith the combined hide expression - Liveness check validators added if the ID owner must be alive