Individuals
Overview
Individual endpoints support creating and working with individual records involved in verification and compliance workflows.
Individual FICA Guide
Table of Contents
- Overview
- Authentication
- API Workflow
- Step-by-Step Implementation Guide
- Endpoint Reference
- Request/Response Examples
- Error Handling
- Best Practices
Overview
The eFICA Individual API allows you to create and manage FICA (Financial Intelligence Centre Act) compliance records for individual clients. This API provides a complete workflow from initial client registration through document verification to final onboarding decisions.
Key Features
- Complete FICA Workflow: End-to-end individual client onboarding
- AML Screening: Automated Anti-Money Laundering checks
- External Validation: Integration with credit bureaus for identity verification
- Document Management: Upload, view, and manage client documents
- PEP & Sanctions Screening: Politically Exposed Persons and sanctions list checks
- Onboarding Decisions: Approve, decline, or refer clients for review
Base URLs
- Sandbox:
https://sandboxapi.efica.co.za - Production:
https://loginapi.efica.co.za
API Version
All endpoints are under /api/v1/individual
Authentication
All endpoints require OAuth2 Bearer token authentication. Include the token in the Authorization header:
Authorization: Bearer <your-oauth2-token>
For details on obtaining OAuth2 tokens, refer to the OAuth2 Integration Guide.
Important: If there is insufficient balance in your account, the individual record will not be created in Step 1.
API Workflow
The following diagram illustrates the complete workflow for onboarding an individual client:
Step-by-Step Implementation Guide
Step 1: Create Individual Record (Mandatory)
Endpoint: POST /api/v1/individual
Create a new individual client record. This is the first step in the onboarding process.
Important Notes:
- If your account has insufficient balance, the record will not be created
- The response contains the individual
id(UUID) which is required for all subsequent steps - Store this
idfor use in all following API calls
Request Body: See Create Individual Request section below.
Response: Returns the individual UUID as id
{
"id": "123e4567-e89b-12d3-a456-426614174000"
}
Step 2: Execute AML Screening (Mandatory)
Endpoint: POST /api/v1/individual/:id/aml-screening
Run an Anti-Money Laundering screening search on the client. This uses data from the individual record created in Step 1 (firstName, lastName, clientEmail).
Path Parameters:
id(string, required): The individual UUID from Step 1
Request Body: None (uses data from individual record)
Response: Returns AML screening results including matched entities and web search results.
{
"matchedNumber": 0,
"matchedEntities": [],
"webSearchResults": []
}
Step 3: Get Consumer Details via External Validation (Mandatory)
Endpoint: POST /api/v1/individual/:id/external-validation
Execute external client validation to retrieve consumer details from credit bureaus. This provides KYC (Know Your Customer) results and consumer information.
Path Parameters:
id(string, required): The individual UUID from Step 1
Request Body: Optional validation parameters. If not provided, uses data from the individual record.
Response: Returns consumer details, KYC results, and employment history.
{
"consumerDetail": {
"FirstName": "JOHN",
"Surname": "DOE",
"IDNo": 7510315000081,
"BirthDate": "1975-10-31T00:00:00+02:00",
"Gender": "Male",
"EmailAddress": "johndoe@gmail.com"
},
"kycResult": {
"IDStatusDesc": "ID Validated On Bureau Data",
"KYCStatusDesc": "No Match Established Within Provided Parameters"
},
"consumerEmploymentHistory": []
}
Step 4: Review KYC, PEP & Sanctions Data (Mandatory)
Manual Review Process: Review the data returned from Steps 2 and 3, including:
- AML screening results
- KYC validation results
- PEP (Politically Exposed Persons) status
- Sanctions list matches
Endpoint: PATCH /api/v1/individual/:id/pep-sanctions-decision
Update the PEP and sanctions decision based on your review.
Path Parameters:
id(string, required): The individual UUID from Step 1
Request Body: See PEP & Sanctions Decision Request section below.
Response: Returns updated individual record.
Step 5: Upload Documents (Mandatory)
Endpoint: POST /api/v1/individual/:id/documents
Upload client documents. You can upload a single file and associate it with multiple document groups and types to save storage space.
Path Parameters:
id(string, required): The individual UUID from Step 1
Request: Multipart form data
file(binary, required): Document file to uploaddocuments(string, required): JSON string array of document group-type pairsadditionalFicaDoc(string, optional): Additional FICA document flag
Example documents field:
[
{"individualDocsID": 1, "IndividualDocTypesID": 1},
{"individualDocsID": 7, "IndividualDocTypesID": 19}
]
Response: Returns created document records with FICA document IDs.
{
"assignedGroups": [
{
"groupId": 1,
"types": [
{
"typeId": 1,
"ficaIndividualDocsID": 123
}
]
}
],
"documents": [],
"count": 2
}
Important:
- Maximum file size: 10MB (default, configurable via
MAX_FILESIZE_MBenvironment variable) - Supported formats: PDF, images (PNG, JPEG, JPG)
- Each document pair creates a separate record pointing to the same uploaded file
Step 6: Document Management (Optional, then Mandatory Confirmation)
6a. View Uploaded Document (Optional)
Endpoint: GET /api/v1/individual/:id/documents/:ficaIndividualDocsID
Retrieve a document as a base64-encoded string.
Path Parameters:
id(string, required): The individual UUIDficaIndividualDocsID(number, required): FICA Individual Document ID from Step 5 response
Response: Returns base64-encoded document content.
{
"ficaIndividualDocsID": 123,
"base64Content": "JVBERi0xLjQKJeLjz9MK...",
"mimeType": "application/pdf",
"filename": "document.pdf"
}
6b. Delete Document (Optional)
Endpoint: DELETE /api/v1/individual/:id/documents/:ficaIndividualDocsID
Delete an incorrectly uploaded document.
Path Parameters:
id(string, required): The individual UUIDficaIndividualDocsID(number, required): FICA Individual Document ID to delete
Response: Returns deleted document information.
6c. Confirm Documents Uploaded (Mandatory)
Endpoint: PATCH /api/v1/individual/:id/confirm-docs
Confirm that all required documents have been uploaded and reviewed. This sets docsUploadedReviewed to true.
Path Parameters:
id(string, required): The individual UUID
Request Body:
{
"userUUID": "123e4567-e89b-12d3-a456-426614174000"
}
Response: Confirmation message and status.
{
"message": "Documents confirmed successfully",
"docsUploadedReviewed": true
}
Step 7: Update Onboarding Decision (Mandatory)
Endpoint: PATCH /api/v1/individual/:id/onboarding-decision
Determine the final onboarding decision: approve, decline, or refer the client for review.
Path Parameters:
id(string, required): The individual UUID
Request Body: See Onboarding Decision Request section below.
Decision Options:
Approved: Client is approved and onboardedDeclined: Client is declinedRefer: Client is referred to another admin for review (will return to this step)In Progress: Decision is still pending
Response: Success message.
{
"message": "The client was successfully onboarded."
}
Note: If a client is referred, they will return to this step after review.
Step 8: Get PDF Report (Optional)
Endpoint: GET /api/v1/individual/:id/pdf-report
Retrieve the complete FICA application report as a PDF (base64-encoded).
Path Parameters:
id(string, required): The individual UUID
Response: Base64-encoded PDF content.
{
"base64Content": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlIC9DYXRhbG9nCi9QYWdlcyAyIDAgUgo+PgplbmRvYmoK...",
"mimeType": "application/pdf",
"filename": "fica-report-123e4567-e89b-12d3-a456-426614174000.pdf"
}
Endpoint Reference
Additional Endpoints
List Individuals with Pagination and Filters
Endpoint: GET /api/v1/individual
Retrieve a paginated list of individuals for the authenticated partner with optional filtering. This endpoint is useful for searching and browsing individual records.
Query Parameters:
Pagination:
take(number, optional): Number of records to return (default: 50, maximum: 100)skip(number, optional): Number of records to skip for pagination (default: 0)
Filters (all optional, can be combined):
externalUserID(string): Filter by external user ID (partial match)clientReference(string): Filter by client reference (partial match)firstName(string): Filter by first name (partial match)lastName(string): Filter by last name (partial match)ficaStatus(string): Filter by FICA status (partial match)identificationNumber(string): Filter by identification number (partial match)passportNumber(string): Filter by passport number (partial match)riskDescription(string): Filter by risk description (partial match)approvalDate(string, ISO date): Filter by approval date (matches entire day)lastUpdated(string, ISO date): Filter by last updated date (matches entire day)nextFicaReviewDate(string, ISO date): Filter by next FICA review date (matches entire day)
Response: Returns paginated list with count.
{
"data": [
{
"ficaIndividualGUID": "123e4567-e89b-12d3-a456-426614174000",
"externalUserID": "EXT-12345",
"clientReference": "REF123456",
"firstName": "John",
"lastName": "Doe",
"ficaStatus": "Approved",
"identificationNumber": "7510315073000",
"passportNumber": "A12345678",
"riskDescription": "Low Risk",
"approvalDate": "2025-12-01T00:00:00.000Z",
"approvedBy": "John Admin",
"lastUpdated": "2025-12-01T10:30:00.000Z",
"nextFicaReviewDate": "2026-12-01T00:00:00.000Z"
}
],
"count": 150,
"take": 50,
"skip": 0
}
Example Requests:
# Get first 50 individuals
GET /api/v1/individual?take=50&skip=0
# Search by first name
GET /api/v1/individual?firstName=John
# Filter by status and risk
GET /api/v1/individual?ficaStatus=Approved&riskDescription=Low Risk
# Filter by approval date
GET /api/v1/individual?approvalDate=2025-12-01T00:00:00.000Z
# Combined filters with pagination
GET /api/v1/individual?firstName=John&lastName=Doe&take=100&skip=0
Important Notes:
- Maximum 100 results per page (enforced automatically)
- Text filters use partial matching (contains)
- Date filters match the entire day (00:00:00 to 23:59:59)
- Results are ordered by
lastUpdateddate (descending) - Only returns individuals for the authenticated partner
- All filters are optional and can be combined
Get Individual Details
Endpoint: GET /api/v1/individual/:id
Retrieve individual client details by UUID.
Path Parameters:
id(string, required): The individual UUID
Response: Complete individual record data.
Update Individual Details
Endpoint: PATCH /api/v1/individual/:id
Update individual client information.
Path Parameters:
id(string, required): The individual UUID
Request Body: See Update Individual Request section below.
Request/Response Examples
Create Individual Request
{
"externalSystemId": "EXT-12345",
"clientRef": "REF123456",
"firstName": "John",
"lastName": "Doe",
"clientEmail": "john.doe@example.com",
"telNumber": "+27123456789",
"userID": "123e4567-e89b-12d3-a456-426614174000",
"idNumber": "7510315073000",
"passportNumber": "A12345678",
"taxNumber": "1234567890",
"addressLineOne": "123 Main Street",
"addressLineTwo": "Apt 4B",
"townCity": "Cape Town",
"zipCode": "8001",
"employmentStatus": 5,
"occupation": "Software Engineer",
"employmentIndustry": 1,
"employerName": "Tech Corp",
"employerIndustry": "Technology",
"transactionFrequency": 1,
"transactionType": 31,
"transactionFundingSource": 32,
"transactionSourceWealth": 8,
"sourceFundingDescription": "Salary",
"sourceWealthDescription": "Savings",
"additionalVerification": false,
"additionalVerificationReason": null,
"transactionConsistent": "true",
"transactionInconsistantReason": null,
"customerPermission": true,
"partnerID": "889912d3-xxxx-xxxx-xxx-b77bb61231c1",
"countryResidence": 2,
"passportCountry": 0,
"addressCountry": 0,
"clientFaceToFace": true,
"clientSACitizen": true,
"customQuestions": {}
}
List Individuals Request Examples
Basic pagination:
GET /api/v1/individual?take=50&skip=0
Filter by name:
GET /api/v1/individual?firstName=John&lastName=Doe
Filter by status and risk:
GET /api/v1/individual?ficaStatus=Approved&riskDescription=Low Risk
Filter by date:
GET /api/v1/individual?approvalDate=2025-12-01T00:00:00.000Z
Combined filters with pagination:
GET /api/v1/individual?externalUserID=EXT-123&ficaStatus=Approved&take=100&skip=50
Update Individual Request
{
"firstName": "John",
"lastName": "Doe",
"clientEmail": "john.doe@example.com",
"clientPhone": "+27123456789",
"externalSystemId": "EXT-12345"
}
External Validation Request (Optional)
{
"identificationNumber": "0101010000081",
"passportNumber": "A12345678",
"firstName": "John",
"lastName": "Doe",
"residentialLine1": "123 Main Street",
"residentialLine2": "Apt 4B",
"residentialLine3": "Suburb",
"residentialZIPCode": "2000"
}
PEP & Sanctions Decision Request
{
"pepConfirmReviewed": true,
"adverseSearchKYC": false,
"clientForeignOfficial": false,
"clientForeignOfficialType": null,
"userID": "123e4567-e89b-12d3-a456-426614174000",
"partnerID": "123e4567-e89b-12d3-a456-426614174001"
}
Field Descriptions:
pepConfirmReviewed(boolean, required): Confirms PEP and sanctions have been reviewedadverseSearchKYC(boolean, required): Are there adverse search results?clientForeignOfficial(boolean, required): Is the client a foreign official?clientForeignOfficialType(number, optional): Required ifclientForeignOfficialistrueuserID(UUID, required): UUID of the user making the decisionpartnerID(UUID, required): UUID of the partner company
Document Upload Request (Multipart Form Data)
Form Fields:
file: Binary file (PDF, PNG, JPEG, JPG)documents: JSON string:[{"individualDocsID": 1, "IndividualDocTypesID": 1}]additionalFicaDoc: Optional string (e.g.,"false")
cURL Example:
curl -X POST "https://sandboxapi.efica.co.za/api/v1/individual/123e4567-e89b-12d3-a456-426614174000/documents" \
-H "Authorization: Bearer <your-token>" \
-F "file=@/path/to/document.pdf" \
-F "documents=[{\"individualDocsID\": 1, \"IndividualDocTypesID\": 1}]" \
-F "additionalFicaDoc=false"
Confirm Documents Request
{
"userUUID": "123e4567-e89b-12d3-a456-426614174000"
}
Onboarding Decision Request
{
"onboardingDecision": "Approved",
"additionalComments": "All checks passed",
"nextFicaReviewDate": "2025-12-02T22:00:00.000Z",
"userID": "123e4567-e89b-12d3-a456-426614174000",
"partnerID": "123e4567-e89b-12d3-a456-426614174000"
}
Decision Values:
"Approved": Client approved"Declined": Client declined"Refer": Referred for review"In Progress": Decision pending
Error Handling
Common HTTP Status Codes
| Status Code | Description | Common Causes |
|---|---|---|
| 200 | Success | Request completed successfully |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request data, validation failed, file too large |
| 401 | Unauthorized | Missing or invalid OAuth2 token |
| 404 | Not Found | Individual or document not found |
| 500 | Internal Server Error | Server error, external service failure |
Error Response Format
{
"statusCode": 400,
"message": "Bad Request",
"error": "Validation failed"
}
Common Error Scenarios
1. Insufficient Balance (Step 1)
If your account has insufficient balance, the individual record will not be created. Check your account balance before attempting to create records.
2. File Upload Errors
File Too Large:
{
"status": 400,
"error": "File size (52428800 bytes) exceeds the maximum allowed size of 10MB"
}
No File Uploaded:
{
"status": 400,
"error": "No file uploaded. Please ensure you are sending a file with the field name \"file\"."
}
3. Invalid Document JSON
{
"status": 400,
"error": "Invalid JSON format for documents field",
"details": "Unexpected token..."
}
4. Missing Required Fields
{
"statusCode": 400,
"message": "Bad Request",
"error": "Validation failed"
}
5. Individual Not Found
{
"statusCode": 404,
"message": "Individual not found"
}
Error Handling Best Practices
- Always check response status codes before processing responses
- Implement retry logic for 500 errors (with exponential backoff)
- Validate file sizes before upload
- Handle insufficient balance gracefully in Step 1
- Log error responses for debugging
- Display user-friendly error messages based on status codes
Best Practices
1. Workflow Adherence
- Follow the workflow sequence: Steps must be completed in order
- Don't skip mandatory steps: All mandatory steps must be completed
- Store the individual UUID: Save the
idfrom Step 1 for all subsequent calls
2. Error Handling
- Check account balance before Step 1
- Implement proper error handling for all API calls
- Retry failed requests where appropriate (500 errors)
- Validate data before sending requests
3. Document Management
- Upload documents in correct format: PDF, PNG, JPEG, JPG
- Respect file size limits: Default 10MB, check configuration
- Use document groups efficiently: One file can serve multiple document types
- Verify uploads: Use GET endpoint to confirm document was uploaded correctly
4. Security
- Never expose OAuth2 tokens: Store securely, never in client-side code
- Use HTTPS: Always use encrypted connections
- Validate responses: Don't trust client-side validation alone
- Implement token refresh: Handle token expiration gracefully
5. Performance
- Batch operations where possible: Upload multiple document types with one file
- Cache individual UUIDs: Store locally to avoid unnecessary GET requests
- Implement request timeouts: Prevent hanging requests
- Use async/await: Handle asynchronous operations properly
6. Testing
- Test in sandbox first: Always test in sandbox environment before production
- Test error scenarios: Test insufficient balance, invalid data, etc.
- Test file uploads: Verify different file types and sizes
- Test workflow completion: Ensure entire workflow can be completed
7. Data Management
- Store individual UUIDs: Keep track of created individual records
- Store document IDs: Save
ficaIndividualDocsIDvalues for document operations - Log important events: Track workflow progress for debugging
- Handle referrals: Implement logic to handle referred clients returning to Step 7
8. List and Search Operations
- Use pagination efficiently: Request only the number of records you need (max 100 per page)
- Combine filters: Use multiple filters together to narrow down results
- Cache search results: Store frequently accessed individual GUIDs locally
- Handle large result sets: Use
skipparameter to paginate through all results - Date filtering: When filtering by dates, the API matches the entire day, so you don't need to specify exact times
Complete Workflow Example
List Individuals Example
Here's an example of listing and searching individuals:
// List all individuals (first page)
const listResponse = await fetch('https://sandboxapi.efica.co.za/api/v1/individual?take=50&skip=0', {
headers: { 'Authorization': `Bearer ${token}` }
});
const listData = await listResponse.json();
console.log(`Found ${listData.count} individuals, showing ${listData.data.length}`);
// Search by name
const searchResponse = await fetch('https://sandboxapi.efica.co.za/api/v1/individual?firstName=John&lastName=Doe', {
headers: { 'Authorization': `Bearer ${token}` }
});
const searchData = await searchResponse.json();
// Filter by status and risk
const filterResponse = await fetch('https://sandboxapi.efica.co.za/api/v1/individual?ficaStatus=Approved&riskDescription=Low Risk&take=100', {
headers: { 'Authorization': `Bearer ${token}` }
});
const filterData = await filterResponse.json();
// Paginate through all results
let allIndividuals = [];
let skip = 0;
const pageSize = 100;
do {
const pageResponse = await fetch(`https://sandboxapi.efica.co.za/api/v1/individual?take=${pageSize}&skip=${skip}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const pageData = await pageResponse.json();
allIndividuals = allIndividuals.concat(pageData.data);
skip += pageSize;
} while (allIndividuals.length < pageData.count);
Complete Onboarding Workflow Example
Here's a complete example of implementing the full workflow:
// Step 1: Create Individual
const createResponse = await fetch('https://sandboxapi.efica.co.za/api/v1/individual', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(individualData)
});
const { id } = await createResponse.json();
// Step 2: AML Screening
await fetch(`https://sandboxapi.efica.co.za/api/v1/individual/${id}/aml-screening`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` }
});
// Step 3: External Validation
const validationResponse = await fetch(`https://sandboxapi.efica.co.za/api/v1/individual/${id}/external-validation`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(validationData)
});
const validationResults = await validationResponse.json();
// Step 4: Review data, then update PEP & Sanctions Decision
await fetch(`https://sandboxapi.efica.co.za/api/v1/individual/${id}/pep-sanctions-decision`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(pepSanctionsData)
});
// Step 5: Upload Documents
const formData = new FormData();
formData.append('file', fileBlob);
formData.append('documents', JSON.stringify([
{ individualDocsID: 1, IndividualDocTypesID: 1 }
]));
const uploadResponse = await fetch(`https://sandboxapi.efica.co.za/api/v1/individual/${id}/documents`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: formData
});
const { documents } = await uploadResponse.json();
// Step 6: Confirm Documents
await fetch(`https://sandboxapi.efica.co.za/api/v1/individual/${id}/confirm-docs`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ userUUID: userId })
});
// Step 7: Onboarding Decision
await fetch(`https://sandboxapi.efica.co.za/api/v1/individual/${id}/onboarding-decision`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
onboardingDecision: 'Approved',
userID: userId,
partnerID: partnerId
})
});
// Step 8: Get PDF Report (Optional)
const pdfResponse = await fetch(`https://sandboxapi.efica.co.za/api/v1/individual/${id}/pdf-report`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const pdfData = await pdfResponse.json();
Support
For additional support or questions:
- Email: melissa@efica.co.za
- Documentation: Check the main API Documentation
Last Updated: 20 January 2026