Natural Person Object
Submits the registration of a natural person for fraud and KYC analysis. QI Tech runs your decision tree against the submitted data and returns in analysis_status the result that your policy determined — see Status dynamics.
There are only 3 required fields. Go straight to Minimum payload and then add whatever makes sense for your use case.
The data you submit must be definitive. CPF, name and date of birth should not change after this call — this ensures consistency of the anti-fraud database and a realistic risk assessment.
The keys returned by the SDKs go into the face, documents and source blocks of this payload. Where each one goes — and what changes compared to Legal Person — is covered in SDK data (face, documents and device).
Minimum payload
This is the smallest body accepted by POST /onboarding/natural_person.
{
"id": "12345678",
"registration_date": "2026-08-07T11:37:15-03:00",
"document_number": "111.111.111-11"
}
Response:
{
"id": "12345678",
"analysis_status": "automatically_approved",
"reason": "rule_decision_enum"
}
The more data you send, the more validations the rules engine is able to perform.
Submit a registration
Query parameters
analyze boolean optional — defaults totrue
With true, your decision tree is executed and the response returns the result. With false, the registration is only recorded (with no charge) and becomes part of the history used in future analyses — the response returns not_analysed.
Request examples
- Python
- PHP
- Node.js
- Java
- C#
- curl
import requests
BASE_URL = "https://api.sandbox.caas.qitech.app"
API_KEY = "YOUR_API_KEY"
payload = {
"id": "12345678",
"registration_date": "2026-08-07T11:37:15-03:00",
"document_number": "111.111.111-11"
}
response = requests.post(
f"{BASE_URL}/onboarding/natural_person",
params={"analyze": "true"},
json=payload,
headers={"Authorization": API_KEY},
timeout=30,
)
response.raise_for_status()
result = response.json()
print(result["analysis_status"]) # automatically_approved
<?php
$baseUrl = 'https://api.sandbox.caas.qitech.app';
$apiKey = 'YOUR_API_KEY';
$payload = [
'id' => '12345678',
'registration_date' => '2026-08-07T11:37:15-03:00',
'document_number' => '111.111.111-11'
];
$ch = curl_init($baseUrl . '/onboarding/natural_person?analyze=true');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: ' . $apiKey
],
CURLOPT_POSTFIELDS => json_encode($payload)
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("Onboarding returned HTTP {$status}: {$body}");
}
$result = json_decode($body, true);
echo $result['analysis_status'];
const BASE_URL = "https://api.sandbox.caas.qitech.app";
const API_KEY = "YOUR_API_KEY";
const payload = {
id: "12345678",
registration_date: "2026-08-07T11:37:15-03:00",
document_number: "111.111.111-11"
};
async function createRegistration() {
const response = await fetch(
`${BASE_URL}/onboarding/natural_person?analyze=true`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: API_KEY
},
body: JSON.stringify(payload)
},
);
if (!response.ok) {
throw new Error(`Onboarding returned HTTP ${response.status}`);
}
const result = await response.json();
console.log(result.analysis_status);
return result;
}
createRegistration();
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class CreateNaturalPerson {
private static final String BASE_URL = "https://api.sandbox.caas.qitech.app";
private static final String API_KEY = "YOUR_API_KEY";
public static void main(String[] args) throws Exception {
String payload = """
{
"id": "12345678",
"registration_date": "2026-08-07T11:37:15-03:00",
"document_number": "111.111.111-11"
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/onboarding/natural_person?analyze=true"))
.header("Content-Type", "application/json")
.header("Authorization", API_KEY)
.timeout(Duration.ofSeconds(30))
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new IllegalStateException(
"Onboarding returned HTTP " + response.statusCode() + ": " + response.body());
}
System.out.println(response.body());
}
}
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class CreateNaturalPerson
{
private const string BaseUrl = "https://api.sandbox.caas.qitech.app";
private const string ApiKey = "YOUR_API_KEY";
public static async Task Main()
{
var payload = new
{
id = "12345678",
registration_date = "2026-08-07T11:37:15-03:00",
document_number = "111.111.111-11"
};
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
client.DefaultRequestHeaders.Add("Authorization", ApiKey);
var content = new StringContent(
JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await client.PostAsync(
$"{BaseUrl}/onboarding/natural_person?analyze=true", content);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(
$"Onboarding returned HTTP {(int)response.StatusCode}: {body}");
}
Console.WriteLine(body);
}
}
curl -X POST \
'https://api.sandbox.caas.qitech.app/onboarding/natural_person?analyze=true' \
-H 'Content-Type: application/json' \
-H 'Authorization: YOUR_API_KEY' \
-d '{
"id": "12345678",
"registration_date": "2026-08-07T11:37:15-03:00",
"document_number": "111.111.111-11"
}'
Response
id sent in the request.analysis_statusenumResult of running your decision tree. See Status dynamics.reasonstringReason for the decision, when available.{
"id": "12345678",
"analysis_status": "automatically_approved",
"reason": "rule_decision_enum"
}
When the analysis takes longer than expected, the response comes back as in_queue or pending and the final result arrives via Webhook. Treat these two statuses as "waiting" — not as a rejection.
Object fields
Required
idstringrequiredIdentifier of the analysis in your system. 1 to 50 characters. Must be unique per request — a repeatedid returns HTTP 409.registration_datedatetimerequiredDate and time of the registration, with time zone. See the accepted format.document_numberstringrequiredCPF with punctuation, in the XXX.XXX.XXX-XX format. Exactly 14 characters.Identification
registration_idstringoptionalIdentifier of the registration in your system. Use the same value across different analyses of the same registration to group them. When omitted, it takes the value ofid.namestringoptionalFull name. 1 to 500 characters.birthdatedateoptionalDate of birth in the YYYY-MM-DD format.genderenumoptionalmale or female.nationalitystringoptionalCountry in ISO 3166-1 alpha-3, 3 uppercase letters. E.g.: BRA.mother_namestringoptionalMother's full name. 1 to 500 characters. A relevant signal for bureau validation.father_namestringoptionalFather's full name. 1 to 500 characters.Financial profile
monthly_incomeintegeroptionalGross monthly income in cents. R$ 5,000.00 →500000.declared_assetsintegeroptionalDeclared assets in cents.occupationstringoptionalOccupation. 1 to 100 characters.is_us_personbooleanoptionalIndicates whether the person has tax obligations in the US (relevant for FATCA).pleaded_pepbooleanoptionalIndicates whether the person declared themselves a politically exposed person (PEP).Contact and location
emailsarrayoptionalList of Email objects. Within each item, onlyemail is required.phonesarrayoptionalList of Phone objects. If sent, each item requires international_dial_code, area_code and number.addressobjectoptionalAddress object. If sent, only postal_code is required within it.documentsobjectoptionalIdentification documents (RG, CNH, passport and others). OCR keys go here — see SDK data and Shared objects.faceobjectoptionalFacial validation data. The key returned by the biometrics SDK goes here — see SDK data.sourceobjectoptionalOrigin of the request (channel, platform, IP, session). This is where the Device Scan session_id goes — see SDK data.Classification and extras
analysis_typestringoptionalType of analysis to apply, when your account has more than one flow configured. Align with support before using it.client_categorystringoptionalCustomer category on your platform or loyalty program. 1 to 100 characters.partnership_keystringoptionalIdentifier of the partnership associated with the registration. 1 to 500 characters.related_account_typestringoptionalType of account related to the registration. 1 to 50 characters.vehicle_platestringoptionalVehicle license plate associated with the registration. 1 to 50 characters.custom_dataobjectoptionalCustom fields for your account. Requires a schema previously registered by QI Tech — see the warning below.{
"id": "12345678",
"registration_id": "cad-98765",
"registration_date": "2026-08-07T11:37:15-03:00",
"analysis_type": "default",
"client_category": "Premium User",
"name": "John Sample",
"document_number": "111.111.111-11",
"birthdate": "1992-09-15",
"gender": "male",
"nationality": "BRA",
"mother_name": "Maria Sample",
"father_name": "John Sample",
"monthly_income": 500000,
"declared_assets": 7500000,
"occupation": "Teacher",
"is_us_person": false,
"pleaded_pep": false,
"emails": [
{
"email": "johnsample@test.com"
}
],
"documents": {
"rg": {
"number": "4.366.477-8",
"issuer": "II",
"issuer_state": "PR",
"issuance_date": "2002-01-12"
},
"cnh": {
"register_number": "05163811694",
"issuer_state": "PR",
"first_issuance_date": "2011-03-21",
"issuance_date": "2016-06-29",
"expiration_date": "2031-06-25",
"category": "AB"
}
},
"address": {
"street": "Rua do Teste",
"number": "111",
"neighborhood": "Bairro do Exemplo",
"city": "Aparecida de Goiânia",
"uf": "GO",
"complement": "Térreo",
"postal_code": "00000-000",
"country": "BRA"
},
"phones": [
{
"international_dial_code": "55",
"area_code": "11",
"number": "999999999",
"type": "mobile"
}
],
"source": {
"channel": "app",
"platform": "android",
"ip": "255.201.26.1",
"session_id": "54b8e3cf-15de-41e5-9305-0ecf059d6e2a",
"os_version": "14"
},
"face": {
"type": "zaig_sdk",
"registration_key": "46f38cf4-07b2-4de6-93e9-64b51a68378a"
}
}
The schema uses additionalProperties: false. Any field outside the ones listed above makes the request return HTTP 400, even if the rest of the payload is correct.
custom_data requires its own schemacustom_data is validated against a schema specific to your company, registered by QI Tech. If you send this field without having the schema registered, the response is HTTP 400 with the message "Custom data not available for you account". Talk to support before using it.
Field formats
registration_date format
ISO 8601 format with a mandatory time zone. The validator accepts offsets ending in :00 or :30, or the Z suffix:
2026-08-07T11:37:15-03:00 ✅
2026-08-07T11:37:15.123456-03:00 ✅ fraction of 1 to 6 digits
2026-08-07T14:37:15Z ✅ UTC
2026-08-07T11:37:15 ❌ no time zone
2026-08-07T11:37:15-03:15 ❌ offset not allowed
document_number — CPF
Must be sent with punctuation: XXX.XXX.XXX-XX, exactly 14 characters. Sending digits only (11111111111) returns HTTP 400.
postal_code — CEP
Inside address, the postal code requires the XXXXX-XXX format (with a hyphen). 00000000 is rejected.
Monetary values
monthly_income and declared_assets are integers in cents of Brazilian reais. Multiply by 100: R$ 5,000.00 → 500000.
Enumerators
gender
| Value | Meaning |
|---|---|
male | Male |
female | Female |
phones[].type
| Value | Meaning |
|---|---|
mobile | Mobile |
residential | Residential |
commercial | Commercial |
| visit | Confirmed by an in-person visit |
| zaig_sdk | Confirmed by QI Tech's SDK |
| zaig_ocr | Confirmed by OCR of a proof document |
face.type
| Value | Meaning |
|---|---|
zaig_sdk | Capture via QI Tech's SDK (use registration_key) |
base_64 | Image sent directly in the image field |
For analysis_status, client_status and risk_level, see Status dynamics.
Testing in Sandbox
In Sandbox the decision is deterministic, defined by the first digit of the CPF:
| First digit | Result |
|---|---|
9 | automatically_approved |
8 | automatically_reproved |
7 | pending |
6 | Manual analysis, with later approval |
5 | Manual analysis, with later rejection |
4 | automatically_challenged |
0 to 3 | in_manual_analysis |
Do not use real natural person data in the Sandbox environment.
Errors
| Status | Situation | How to resolve |
|---|---|---|
| 400 | Missing required field, invalid format, enum outside the list or field not defined in the schema. | Check the description in the response, which points to the field. |
| 400 | custom_data without a registered schema. | Ask support to register the schema. |
| 401 | Missing Authorization header or deactivated API Key. | Check the key. |
| 403 | Invalid API Key. | Confirm the key with support. |
| 409 | id already used. | Generate a unique id per request. |
| 500 | Internal error. | Our specialists are notified automatically. |
Full list in HTTP Status.
Integration checklist
-
POST /onboarding/natural_personwith the minimum payload returning200in Sandbox. -
idunique per request (test the409by resending the sameid). -
registration_idstable so analyses of the same registration are grouped. - CPF with punctuation and postal code with a hyphen.
- Monetary values in cents.
-
in_queueandpendingtreated as "waiting", not as a rejection. - Webhook configured to receive the asynchronous result.