Sending a Lead
Submit an applicant that FincheckEngine matches and sends to partners for you.
POST /api/v2/lead is the Direct integration. You send the applicant once, and FincheckEngine validates them, matches them to eligible partners, and sends the lead to those partners in the background.
See POST /api/v2/lead in the API Reference →
Before you begin
- You have a v2 API key for the environment you are calling. See the Quickstart.
- You know the intent (product) the applicant wants, and you have read its current rules. List the intents and their rules with
GET /api/v2/intent; see How your request is checked. - The applicant has consented to their data being processed (POPI).
1. Send the applicant
curl -X POST "https://api.engine-v2.uat.fincheck.co.za/api/v2/lead" \
-H "Authorization: Bearer fce_your_key" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Thabo",
"last_name": "Molefe",
"cell_phone_number": "0821234567",
"email": "thabo.molefe@example.com",
"id_number": "8001015009087",
"intent": "Personal Loans",
"popi": true,
"gross_income": 25000,
"net_income": 18000,
"loan_amount_required": 5000
}'
This example includes the income and loan fields that Personal Loans needs; other intents need different fields. Below are the rules every lead follows. Send either intent or intent_id.
The controls on this endpoint are intent / intent_id, referee_id and partner_ids (see Send to specific partners). fast_application belongs to POST /offer: if you send it here it is checked for type but has no effect.
How your request is checked
Every lead is checked against the rules in the table above, plus the rules of the intent you select: the extra fields it requires, any narrower limits, and any fields only that intent defines. Together they are the validation contract for that request.
Read the contract from the API, every time it matters. Call GET /api/v2/intent and use each intent's schema for its current required fields, limits and intent-specific fields. To read just one intent, call GET /api/v2/intent/{id} with its id. Do not copy an intent's rules into your integration: they can change without a new API version, and a request that was valid yesterday can be rejected today.
{
"id": 2,
"title": "Personal Loans",
"schema": {
"required": ["first_name", "last_name", "cell_phone_number", "id_number", "popi", "gross_income"],
"properties": { "repayment_period": { "maximum": 72 } }
}
}
This is an illustrative excerpt: required lists every field you must send for that intent, and properties carries each field's limits. A required field you leave out is rejected with required; a value outside a limit is rejected with the matching code. See Intent fields for how to read the whole schema.
Every problem found in the body is reported at once. Each field lists the first problem found with it.
Choosing the intent
Send intent (the title) or intent_id (the id), both from GET /intent. You may send both when they name the same intent.
An intent error is the whole answer: field problems are reported once the intent is accepted.
Names, email, mobile and ID numbers
- Names (
first_name,last_name): letters from any alphabet, spaces, hyphens, straight or curly apostrophes and full stops, with at least one letter.Zoë,Ané,D’Angelo,Anne-MarieandC.J.are all accepted. email: an ASCII address with a dotted domain, such asperson@example.comorperson@company.co.za.user@localhostis rejected, and addresses with non-ASCII characters are not supported.cell_phone_number: a South African mobile number (06x, 07x or 081–084), written as0821234567,821234567,27821234567or+27821234567. Spaces, dashes, dots and brackets are allowed, so+27 82 123 4567is accepted. It is stored as0821234567.id_number: a valid South African ID number, 13 digits, for an applicant aged 18–100.
Send every field with its JSON type: numbers for money fields (25000, not "25000"; decimals are rounded to whole rand), whole numbers for counts, true/false for yes-no fields, and strings for text, including id_number and cell_phone_number. A value of the wrong type is rejected with 422. Omit optional fields when no value is available. A null or blank value counts as not sent; for a required field that is reported as required. See Placeholder values if your system uses a placeholder instead. Send the body as Content-Type: application/json; anything else returns 415.
Send what you have: more data gives partners more to match on.
Placeholder values
Leave out fields you don't have, or send JSON null. If your system fills a missing value with a placeholder, v2 also accepts the placeholders below, on the fields listed below, and treats them as not provided. Use only these values:
Placeholders match in any letter case, and spaces before or after are ignored. Other spellings, such as none, na, -, or 0, are validated as real values, so "employed": "none" is rejected.
These fields accept placeholders:
These fields don't treat placeholders as missing:
A placeholder counts as a missing value, so the rules for missing values apply: for example, if net_income is a placeholder and gross_income is sent, net_income defaults to gross_income.
2. Store the lead id
A successful request returns HTTP 200:
{
"id": "k3mN9pQx",
"status": "Lead successfully added",
"result": "created"
}
result is duplicate when you already sent this applicant and the existing lead is returned; see Duplicate submissions.
When you send partner_ids, the response also lists which partners received the lead:
Store id with your own applicant reference. You need it to track the lead and for any support query.
3. Handle validation errors
If a field fails validation, the API returns HTTP 422 and names each field you need to correct, with a stable code and a message:
{
"code": "validation_failed",
"message": "Validation failed.",
"errors": {
"first_name": [{ "code": "invalid_format", "message": "must contain at least one letter, and only letters, spaces, hyphens, apostrophes or full stops" }],
"cell_phone_number": [{ "code": "invalid_format", "message": "must be a South African mobile number (06x, 07x or 081–084)" }],
"gross_income": [{ "code": "invalid_type", "message": "must be a number" }],
"popi": [{ "code": "invalid_value", "message": "Client must give consent." }]
}
}
The keys in errors are the JSON field names you sent, and each lists the first problem found with that field. Branch on code; messages may be reworded. Correct those values and send the lead again. See Errors for every code, and for business-rule failures such as an inactive intent.
Send to specific partners
If Fincheck has agreed a targeted referral with you, send partner_ids to refer the lead only to those partners instead of the partners FincheckEngine would match:
{
"first_name": "Thabo",
"last_name": "Molefe",
"cell_phone_number": "0821234567",
"id_number": "8001015009087",
"intent": "Personal Loans",
"popi": true,
"partner_ids": [227, 311]
}
The request waits for each referral and reports the outcome per partner:
{
"id": "k3mN9pQx",
"status": "Lead successfully added",
"referred_partner_ids": [227],
"errors": [{ "partner_id": 311, "message": "Partner not eligible" }]
}
You can send up to 50 partner ids. The singular partner_id is not supported: sending it with a value returns 422, with partner_id listed as unsupported_field ("is not supported; use partner_ids"). Use partner_ids.
Duplicate submissions
If you send the same applicant again, the response's result says what happened:
duplicate: you already have this lead, and itsidis returned. Nothing is sent again.created: a new lead. When the earlier lead was already sent to partners, the new one only goes to partners that have not received the applicant recently.
A partner never receives the same applicant twice from your partner_ids within its repeat window; such a partner is listed in errors as already sent to this partner. See Duplicate Submissions for the full rules.