Batch sync customers and loans from LMS
curl --request POST \
--url https://api.finosu.com/webhooks/lms/sync/customer-full/batch \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"customerIds": [
"<string>"
]
}
'import requests
url = "https://api.finosu.com/webhooks/lms/sync/customer-full/batch"
payload = { "customerIds": ["<string>"] }
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({customerIds: ['<string>']})
};
fetch('https://api.finosu.com/webhooks/lms/sync/customer-full/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.finosu.com/webhooks/lms/sync/customer-full/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'customerIds' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.finosu.com/webhooks/lms/sync/customer-full/batch"
payload := strings.NewReader("{\n \"customerIds\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.finosu.com/webhooks/lms/sync/customer-full/batch")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"customerIds\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.finosu.com/webhooks/lms/sync/customer-full/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customerIds\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"jobId": "<string>",
"totalCustomers": 123,
"message": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}LMS Sync
LMS Customer Batch Sync
Queue 1–100 external customer IDs for background processing. HTTP 202 acknowledges the job, not successful synchronization.
POST
/
webhooks
/
lms
/
sync
/
customer-full
/
batch
Batch sync customers and loans from LMS
curl --request POST \
--url https://api.finosu.com/webhooks/lms/sync/customer-full/batch \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"customerIds": [
"<string>"
]
}
'import requests
url = "https://api.finosu.com/webhooks/lms/sync/customer-full/batch"
payload = { "customerIds": ["<string>"] }
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({customerIds: ['<string>']})
};
fetch('https://api.finosu.com/webhooks/lms/sync/customer-full/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.finosu.com/webhooks/lms/sync/customer-full/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'customerIds' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.finosu.com/webhooks/lms/sync/customer-full/batch"
payload := strings.NewReader("{\n \"customerIds\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.finosu.com/webhooks/lms/sync/customer-full/batch")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"customerIds\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.finosu.com/webhooks/lms/sync/customer-full/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customerIds\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"jobId": "<string>",
"totalCustomers": 123,
"message": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}Overview
Sync multiple customers and all their loans from your company’s configured LMS in a single request. The endpoint processes customers asynchronously — it immediately returns a job ID (HTTP 202 Accepted) and processes customers in the background. UseGET /webhooks/lms/sync/batch/{jobId} to check the processing status.
Each customer is processed independently. Individual failures do not block the rest.
How It Works
- You send a list of customer IDs from your LMS
- Finosu returns a job ID immediately
- In the background, for each customer ID:
- Fetches customer info from your LMS (name, phone, email, SSN, address, etc.)
- Creates or updates the customer in Finosu
- Fetches all loans for that customer
- Creates loan records and schedules as appropriate
- You poll for status until the job completes
Request Body Format
{
"customerIds": ["12345", "67890", "11111"]
}
Fields
- customerIds - Array of customer identifiers in your LMS (required)
- Minimum: 1 ID
- Maximum: 100 IDs per request
- Each ID must be a non-empty string
Response Format
The endpoint immediately returns HTTP 202 with a job ID:{
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"totalCustomers": 3,
"message": "Batch sync queued. Use GET /webhooks/lms/sync/batch/550e8400-e29b-41d4-a716-446655440000 to check status."
}
Response Fields
- jobId — Unique identifier for the batch job. Use this to poll for status.
- totalCustomers — Total number of customers queued for processing.
- message — Instructions for checking progress.
Checking Job Status
UseGET /webhooks/lms/sync/batch/{jobId} to check progress:
{
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"totalCustomers": 3,
"processed": 2,
"failed": 0,
"progressPercentage": 66.7,
"createdAt": "2026-08-03T12:00:00Z",
"processingStartedAt": "2026-08-03T12:00:01Z",
"processingCompletedAt": null,
"errorMessage": null
}
Status Values
- pending — Job is queued but not yet started
- processing — Job is currently processing customers
- completed — All customers have been processed (some may have failed individually)
- failed — Job encountered a fatal error, or all customers failed
Partial Success Handling
- Individual failures don’t stop processing — If one customer fails (e.g., not found in your LMS), the rest continue
- Detailed tracking — The
failedcount shows how many customers could not be synced - Transaction isolation — Each customer is processed in its own transaction
When to Use Batch vs Single
Use the batch endpoint when:- Syncing multiple customers from your LMS at once
- Performing initial data migration
- Running scheduled sync workflows
- You have more than a few customers to sync
- Syncing one customer in real-time (e.g., webhook from your LMS on application submission)
- Testing or debugging individual customer sync
- You need the sync result immediately in the response
Comparison with POST /customers/batch
| POST /customers/batch | POST /webhooks/lms/sync/customer-full/batch | |
|---|---|---|
| Input | Full customer payload (name, phone, email, address, etc.) | Just customer IDs from your LMS |
| Data source | You provide all fields | Finosu fetches from your LMS |
| Loans | Created separately via POST /loans | Automatically synced from your LMS |
| Schedules | Created based on taskType | Automatically created based on loan status |
| Best for | Companies without an LMS integration | Companies with a configured LMS |
Error Handling
| Status | Meaning |
|---|---|
| 202 | Job queued successfully |
| 401 | Missing or invalid API key |
| 403 | Company not authorized for LMS integration |
| 422 | Validation error (empty list, too many IDs, empty ID) |
| 500 | Failed to queue job |
Authentication
Requires X-API-Key header with a valid webhook API key. The key determines which company and LMS integration to use.Limits
- Maximum 100 customer IDs per request
- Each customer ID must be a non-empty string
Request naming
Use camelCase request fields. Existing snake_case aliases are also accepted by the API.Body
application/json