Finfra API Reference
The Finfra API is a RESTful interface for managing borrower KYC, loan origination, disbursement, and repayment. It accepts JSON and form-encoded requests and returns JSON responses. All actions are performed per object - bulk operations are not supported. The API key type used determines whether requests hit the production environment or are restricted to test mode.
Base URLs
Endpoints at a glance
Authentication
Finfra uses three authentication schemes. Which one to use depends on the endpoint. Keep your API keys confidential - never expose them in public repositories or client-side code. All API interactions must be over HTTPS.
| Scheme | Header | Used for |
|---|---|---|
| BasicAuth | Authorization: Basic … | Generating access tokens |
| BearerAuth | Authorization: Bearer JWT | Borrower, document, and loan endpoints |
| ApiKeyAuth | finfra-auth-key: … | Callback / webhook endpoints |
Generate access token
/api/auth/token (v1) is deprecated. Use /api/auth/token/v2 for all new integrations.Request
curl -X POST https://sandbox-partner-service.elp.finfra.io/api/auth/token/v2 \ -H "Authorization: Basic $(echo -n 'clientId:clientSecret' | base64)"
Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
POST /api/auth/refresh with headers x-access-token and x-refresh-token to refresh your session without re-authenticating with Basic credentials.Error Handling & Healthcheck
All errors return a consistent JSON body. Use the healthcheck endpoints to verify service availability before integration testing.
Error response format
{
"status": 400,
"error": "Bad Request",
"message": "Validation failed: email is required"
}
HTTP status codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Resource created |
| 400 | Validation error - check request body or params |
| 401 | Unauthorized - invalid or missing token |
| 403 | Forbidden - insufficient permissions |
| 409 | Conflict - resource already exists |
| 415 | Unsupported media type |
| 429 | Rate limited - slow down and retry |
| 500 | Internal server error |
Healthcheck
Create Borrower
Finfra supports two borrower types: PERSONAL (individual) and BUSINESS (entity). The type provided drives a different KYC procedural workflow, so ensure the correct value is supplied.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | required | Bearer JWT from /api/auth/token/v2 |
| kyc-id | string | optional | KYC session token for linking existing KYC context |
Request body (JSON)
Top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | required | PERSONAL or BUSINESS. Determines the KYC workflow applied to this borrower. |
| entity | object | optional | Business entity details. Required when type is BUSINESS. |
| authorised_signatory | object | required | Individual person details. For PERSONAL borrowers this is the borrower themselves. For BUSINESS borrowers this is the authorised signatory of the entity. |
entity object
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | required | Registered company name |
| address | object | required | Company registered address. See address object below. |
| phone_number | string | required | Company phone number in international format, e.g. +6287711998822 |
| fax | string | optional | Company fax number in international format |
| string | required | Company email address | |
| business_type | string | required | Type of business. e.g. OTHER |
| industry | string | required | Industry classification. e.g. PRIVATE_PERSON_NAME |
| nationality | string | required | ISO 3166-1 alpha-2 country code of company nationality, e.g. ID |
| residence | string | required | ISO 3166-1 alpha-2 country code of company residence, e.g. ID |
| npwp | string | required | Company tax identification number (NPWP) |
| phone_ext | string | optional | Additional phone number or extension |
| legal_entity | string | required | Legal entity type, e.g. PT, CV |
| establishment_date | string | required | Date of establishment in YYYY-MM-DD format |
| establishment_place | string | required | City or place of establishment |
| deed_date | string | required | Date of the establishment deed in YYYY-MM-DD format |
| deed_number | string | required | Establishment deed reference number |
| managements | array | required | List of company management personnel. See management object below. |
entity.managements[] object
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | required | Full name of the management person |
| position | string | required | Role in the company, e.g. OWNER_MANAGEMENT |
| email_address | string | required | Email address |
| mobile_number | string | required | Mobile number in international format |
| address | string | required | Residential address |
| gender | string | required | MALE or FEMALE |
| identity_number | string | required | NIK (national identity number), 16 digits |
authorised_signatory object
| Field | Type | Required | Description |
|---|---|---|---|
| first_name | string | required | First name |
| last_name | string | required | Last name |
| birth_date | string | required | Date of birth in YYYY-MM-DD format |
| birth_place | string | required | City or place of birth |
| string | required | Email address | |
| mother_maiden_name | string | required | Mother's maiden name |
| marital_status | string | required | MARRIED, SINGLE, DIVORCED, or WIDOWED |
| gender | string | required | MALE or FEMALE |
| title | string | required | Salutation, e.g. BPK (Bapak), IBU |
| domicile | string | required | ISO 3166-1 alpha-2 country code of domicile |
| religion | string | required | Religion, e.g. ISLAM, CHRISTIAN, CATHOLIC, HINDU, BUDDHIST, CONFUCIAN |
| job | string | required | Occupation type, e.g. PRIVATE_SECTOR_EMPLOYEE |
| education | string | required | Highest education level, e.g. ELEMENTARY_SCHOOL |
| address | object | required | Residential address. See address object below. Also accepts country field (ISO 3166-1 alpha-2). |
| mobile_number | string | required | Mobile number in international format |
| bank_account | array | optional | List of bank accounts. See bank_account object below. |
| documents | object | required | KYC documents. See documents object below. |
| business_type | string | optional | Type of business (if self-employed), e.g. OTHER |
| monthly_income_range | string | required | Monthly income bracket, e.g. LESS_THAN_3_MILLION |
| emergency_contact_name | string | required | Emergency contact full name |
| emergency_contact_number | string | required | Emergency contact phone number |
| housing_status | string | required | Housing ownership status, e.g. OWN_HOME |
| source_of_fund | string | required | Primary source of income, e.g. SALARY |
| place_of_work | string | required | Employer or company name |
| work_address | string | required | Workplace address |
| number_of_dependents | integer | optional | Number of financial dependents |
| spouse_identification_number | string | optional | Spouse NIK. Applicable when marital_status is MARRIED. |
| spouse_name | string | optional | Spouse full name |
| spouse_date_of_birth | string | optional | Spouse date of birth in YYYY-MM-DD format |
| prenuptial_agreement | string | optional | true or false. Whether a prenuptial agreement exists. |
address object
| Field | Type | Required | Description |
|---|---|---|---|
| street_address | string | required | Full street address |
| rt | string | required | RT (neighbourhood unit) number |
| rw | string | required | RW (community unit) number |
| province | string | required | Province code, e.g. JAWA_BARAT, DKI_JAKARTA |
| city | string | required | City or regency name |
| district | string | required | District (kecamatan) name |
| village | string | required | Village (kelurahan) name |
| postal_code | string | required | Postal code |
| country | string | optional | ISO 3166-1 alpha-2 country code. Used in authorised_signatory.address. |
bank_account[] object
| Field | Type | Required | Description |
|---|---|---|---|
| holder_name | string | required | Account holder full name |
| number | string | required | Bank account number |
| swift_code | string | required | Bank SWIFT/BIC code |
documents object
| Field | Type | Required | Description |
|---|---|---|---|
| ktp | object | required | Indonesian national ID card (KTP). Contains number (16-digit NIK), file (base64 or URL), issuing_city, and expiry_date (YYYY-MM-DD). |
| selfie | object | required | Selfie photo. Contains file (base64 or URL). |
| npwp | object | optional | Tax ID document. Contains number (NPWP number) and file (base64 or URL). |
| selfie_with_ktp | object | optional | Selfie holding KTP. Contains file (base64 or URL). |
| stock_images | object | optional | Business stock/inventory images. Contains file (base64 or URL). |
| finance_report | object | optional | Financial report document. Contains file (base64 or URL). |
Example request body
{
"type": "PERSONAL",
"authorised_signatory": {
"first_name": "Budi",
"last_name": "Santoso",
"birth_date": "1995-11-01",
"birth_place": "Jakarta",
"email": "budi@example.com",
"mother_maiden_name": "Sari",
"marital_status": "MARRIED",
"gender": "MALE",
"title": "BPK",
"domicile": "ID",
"religion": "ISLAM",
"job": "PRIVATE_SECTOR_EMPLOYEE",
"education": "ELEMENTARY_SCHOOL",
"address": {
"street_address": "Jl. Sudirman No. 1",
"rt": "01",
"rw": "01",
"province": "JAWA_BARAT",
"city": "Bandung",
"district": "Coblong",
"village": "Dago",
"postal_code": "40135",
"country": "ID"
},
"mobile_number": "+6281234567890",
"bank_account": [
{
"holder_name": "Budi Santoso",
"number": "123123123213",
"swift_code": "BNINIDJA"
}
],
"documents": {
"ktp": {
"number": "3275062009921981",
"file": "base64_or_url",
"issuing_city": "Jakarta",
"expiry_date": "2099-11-01"
},
"selfie": { "file": "base64_or_url" }
},
"monthly_income_range": "LESS_THAN_3_MILLION",
"emergency_contact_name": "Humairah",
"emergency_contact_number": "08123456789",
"housing_status": "OWN_HOME",
"source_of_fund": "SALARY",
"place_of_work": "Finfra",
"work_address": "Mega Kuningan",
"number_of_dependents": 3,
"spouse_name": "Mia",
"spouse_identification_number": "3275062309984412",
"spouse_date_of_birth": "1999-03-01",
"prenuptial_agreement": "true"
}
}
Response
{
"status": "success",
"message": "Borrower creation request accepted"
}
Create Borrower (KYC Path)
POST /api/core/users.Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| kycIdPath | string | required | JWT-formatted KYC session ID e.g. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 |
Get Borrower
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Borrower user ID |
Response fields
| Field | Type | Description |
|---|---|---|
| id | string | Unique borrower identifier |
| partner_id | string | Partner identifier (UUID) the borrower belongs to |
| status | string | Current borrower status, e.g. PENDING, APPROVED_BY_PARTNER, BLOCKED |
| type | string | PERSONAL or BUSINESS |
| authorised_signatory | object | Individual person details. Same structure as the Create Borrower request. Includes all personal info, address, bank accounts, documents, employment, and spouse details. |
| created_at | string | ISO 8601 timestamp of when the borrower was created |
Response
{
"id": "6a41ff87dda4eefd0b7b0300",
"partner_id": "1507b694-e78e-4fc9-ac28-68d72fba45e9",
"status": "APPROVED_BY_PARTNER",
"type": "PERSONAL",
"authorised_signatory": {
"first_name": "FERDIAN",
"last_name": "HENDRI SISWORO",
"birth_date": "1987-11-23T00:00:00.000Z",
"birth_place": "MALANG",
"email": "sarengmalang@gmail.com",
"mother_maiden_name": "Lilik Ariani",
"documents": {
"ktp": {
"number": "3573032311870003",
"file": "6a41fddddda4eefd0b7b0274"
},
"selfie": {},
"npwp": {},
"selfie_with_ktp": {}
},
"marital_status": "MARRIED",
"domicile": "ID",
"religion": "CHRISTIAN",
"gender": "MALE",
"job": "BUSINESS_OWNER",
"education": "BACHELORS_DEGREE",
"address": {
"street_address": "JL. DANDELION D2 PERUM DE GREEN PAVILLION",
"city": "MALANG",
"district": "LOWOKWARU",
"postal_code": "65142",
"province": "JAWA_TIMUR",
"rt": "002",
"rw": "003",
"village": "TUNGGULWULUNG"
},
"mobile_number": "+6281339933711",
"bank_account": [
{
"holder_name": "FERDIAN HENDRI SISWORO",
"number": "7353740141",
"swift_code": "BBIJIDJA"
}
],
"business_type": "RESTAURANT",
"monthly_income_range": "GTE_20_AND_LT_50_MILLION",
"emergency_contact_name": "Doni",
"emergency_contact_number": "+6287854595905",
"housing_status": "OWN_HOME",
"source_of_fund": "SALARY",
"place_of_work": "Sareng Indonesian Culinary & Coffee Space",
"work_address": "",
"number_of_dependents": 2,
"spouse_identification_number": "3573012208890003",
"spouse_name": "Yessi Puspitasari",
"spouse_date_of_birth": "1989-08-22T00:00:00.000Z",
"prenuptial_agreement": false
},
"created_at": "2026-06-29T05:15:51.591Z"
}
Update Borrower
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Borrower user ID to update |
Approve Borrower
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Borrower user ID to approve |
APPROVED status before a loan can be created for them.Block Borrower
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Borrower user ID to block |
Borrower Consent
GET /api/core/kyc/consent-list to retrieve the consent items to present to the borrower.Credit Score Report
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| credit-source | string | optional | Comma-separated sources: afpi, clik, or afpi,clik |
Credit History
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| identityNumber | number | required | Borrower NIK (national identity number) |
Verify Borrower Biometric (RDF)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Borrower user ID |
Check Borrower with Hub
callbackTitle specifies which check type to run.Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Borrower user ID |
| callbackTitle | string | required | Type of hub check to perform |
OCR Documents
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| data-source | string | optional | izidata | VERIHUBS | ASLIRI | VIDA |
| kyc-id | string | optional | KYC session token |
Biometric KYC
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| kyc-id | string | optional | KYC session token |
CLIK Credit Report
AFPI Credit History
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| identityNumber | number | required | Borrower NIK |
| reffid | string | required | Reference ID from AFPI |
KYC Consent List
Get KYC Submission
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| kycIdPath | string | required | KYC session JWT token |
Submit KYC
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| kycIdPath | string | required | KYC session JWT token |
Create Document
The KYC document flow is two steps: (1) create a document object to receive a presigned upload URL, then (2) upload the file content to that URL. These steps must be executed in sequence.
Request body: multipart/form-data with document metadata and the file.
presigned_url. The document is not usable until the file upload is complete.Response
{
"id": "doc_01j4abc",
"type": "KTP",
"presigned_url": "https://storage.finfra.io/upload?token=...",
"expires_at": "2025-06-29T09:00:00Z"
}
Get Document
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| documentId | string | required | Document ID (min 6 chars) |
Get Document File
application/octet-stream.Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| documentId | string | required | Document ID (min 6 chars) |
Update Documents
List Loans
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | optional | Filter by borrower user ID |
| uniqueId | string | optional | Filter by loan unique ID (min 6 chars) |
| pageNumber | integer | optional | Page number (default: 1) |
| pageSize | integer | optional | Results per page |
| startDate | string | optional | Filter from date |
| endDate | string | optional | Filter to date |
| startRangeAmount | string | optional | Minimum loan amount filter |
| endRangeAmount | string | optional | Maximum loan amount filter |
Create Single Payment Loan
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Approved borrower user ID |
APPROVED status before a loan can be created. Call POST /api/core/users/{userId}/approve first if needed.Create BNPL (Installment Loan)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Approved borrower user ID |
Loan Detail
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| uniqueId | string | required | Loan unique ID (min 6 chars) |
Initiate Loan Signing
Disburse Loan
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| loanUniqueId | string | required | Loan unique ID |
POST /api/core/loans/signing/initiate before proceeding.Reject Loan
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| loanUniqueId | string | required | Loan unique ID |
Create Payment Link
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| loanUniqueId | string | required | Loan unique ID |
Create Virtual Account
Borrower Status Callback
Finfra sends callbacks to your registered endpoint when borrower status, credit score, loan, or BNPL status changes. Secure these endpoints with the finfra-auth-key header.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| callbackTitle | string | required | Event type identifier (e.g. user_created, loan_disbursed) |
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| finfra-auth-key | string | required | API key for callback authentication (from dashboard) |
Payment Gateway Invoice Webhook
Virtual Account Webhook
Didit Session Callback
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| verificationSessionId | string | optional | Didit verification session ID |
| status | string | optional | Didit verification status (default: 0) |