Account Onboarding (KYC/KYB)
This document describes, step by step, the routes a partner must consume to carry out the complete account opening (KYC) flow at 4Pay from the partner's frontend.
1. Overview
This API allows a partner to build their own onboarding frontend and guide the end customer through the entire 4Pay account opening flow, including identity validation (KYC) and liveness.
The flow consists of multiple sequential steps. Each step must be completed successfully before moving on to the next one.
1.1 Base URLs
Lab (sandbox): https://verify-api.4p.finance/lab/
⚠️ Access to the lab environment
The lab environment requires prior IP whitelisting. Send the origin IP(s) of your requests to the 4Pay team before starting your tests.
Production: https://verify-api.4p.finance/
🔑 Partner ID — production only
The Partner ID segment (hash identifying the partner) is used only in the production environment. In the lab, routes are called directly after /lab/, without this segment.
example (lab): https://verify-api.4p.finance/lab/account/initial_registration
example (production): https://verify-api.4p.finance/v1/{hash-partner-id}/account/initial_registrationIn the following sections, each route's path is shown as relative (e.g., /account/initial_registration). Build the full URL by combining it with the environment's base URL and, in production, adding the Partner ID right after the domain.
1.2 Standard Response Format
All API responses follow the same base format. The most important field for handling the response programmatically is always info.result — it uniquely identifies the outcome of the operation, regardless of the value of success or http_code.
💡 Integration tip
Always base your application's logic on info.result, not just on success or http_code. This is because an http_code of 200 may contain either success or a business-rule failure (e.g., document already registered). The message field is user-friendly and can be shown directly to the end user when appropriate.
1.3 Integration Architecture
🌐 Direct browser calls — no intermediary backend
The partner's frontend must call the 4Pay API directly from the end customer's browser, without going through a partner backend/server as an intermediary. This is an architectural requirement: it's what allows 4Pay to apply IP control and risk analysis via WAF directly on the end user's traffic (e.g., IP reputation lists, proxy/VPN/datacenter detection, per-user rate limiting). If requests go through the partner's backend, that visibility is lost.
The routes described in this document are partner-specific and have CORS enabled only for the domain registered in the integration setup.
1.4 Authentication — reCAPTCHA v3
🔒 reCAPTCHA scope
Only the first route in the flow — /account/initial_registration — requires the reCAPTCHA v3 token (rid field). The other routes in the flow do not require this token.
The token must be generated on the partner's frontend using the exclusive site key provided by 4Pay for that partner.
The domain authorized to generate tokens with this site key is restricted to the partner's registered domain.
The token is validated on 4Pay's backend (minimum score and expected action). See the reCAPTCHA v3 documentation
1.5 Flow Overview
The steps below must be executed in this order:
Initial registration (email and password) — sends an email to confirm ownership of the email address
Account opening hash validation — confirms that the hash received in the URL by email is valid
Document submission (CPF/CNPJ) — checks whether an account already exists with that document
Phone verification code request (WhatsApp or SMS)
Verification code validation sent to the phone
Address data submission
Start of the liveness process (facial validation)
Identity document submission (KYC)
Final account/KYC status check
2. Initial Registration
First step of the flow. Creates the customer's initial registration with email and password.
POST /account/initial_registration
📧 What happens after this call
An email is sent to the customer with a confirmation link. The customer needs to click this link to confirm they own the email address and continue the registration. The link contains an h (hash) parameter that identifies this account opening — this hash will be reused in the following steps.
Payload (body)
Response — Success
Payload fields: email (customer's email), password (password chosen by the customer — see requirements below), newsletter (1 = opted in to communications, 0 = declined), rid (reCAPTCHA v3 token).
Password requirements
The password provided in the password field must meet all of the criteria below:
Minimum of 6 characters.
Maximum of 20 characters.
A mix of uppercase and lowercase letters.
At least one special character (e.g., !, @, #, $).
At least one number.
3. Account Opening Hash Validation
Executed when the customer accesses the URL received by email (after clicking the confirmation link). Validates whether the hash for that account opening exists and returns the data already registered so far.
GET /account/customer_hash/{hash}
🔑 About the hash
This is the same hash used in all other steps of the flow — received in the h parameter of the confirmation URL sent by email. Here it is sent directly in the URL path, not in the body.
Response — Hash found
The hash is valid. The data field returns the customer's data registered so far, including KYC progress (status_kyc and step_kyc).
Response — Hash not found
There is no account opening with this hash (it may have expired, already been completed, or be invalid).
4. Document Submission (Existence Check)
Step executed as soon as the customer clicks the link received by email and fills in the CPF or CNPJ in the partner's form. This call checks whether a 4Pay account already exists associated with that document.
POST /account/update_customer
🔑 About the hash
The hash is the unique, temporary identifier for that account opening. It comes in the h parameter of the URL sent by email (e.g., https://lab-app.4p.finance/complete-registration?h=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6) and must be reused in every subsequent step of the flow. This hash is automatically invalidated once the account is completed.
Payload (body)
Response — Document not found (may proceed)
No account exists with this document. Registration can continue normally.
Response — Document already registered
A 4Pay account already exists with this document. The flow must be stopped and the customer informed.
Payload fields: person_document (CPF or CNPJ, always formatted/masked — e.g., 111.222.333-44), hash (account opening identifier), step_kyc (fixed at 1 for this step).
Note: on this specific route, the result comes in info.details.result (not in info.result as with the other routes).
5. Phone Verification Code Request
⏱️ Code expiration
The verification code sent via WhatsApp or SMS expires in 5 minutes. After this period, a new code must be requested.
✅ WhatsApp is the preferred channel
Validation via WhatsApp is the recommended method and has approximately a 99% delivery success rate. SMS is offered as an alternative, but tends to have delivery issues — use it only as a fallback if WhatsApp is not available for the customer's number.
Sends a verification code to the customer's phone via WhatsApp. Recommended channel.
POST /account/verification_code
Payload (body)
Response — Code sent
6. Verification Code Validation
Validates the code received by the customer via WhatsApp or SMS.
POST /account/validation_code
Payload (body)
Response — Valid code
Response — Invalid code
7. Address Data Submission
Sends the address data and other complementary registration data for the customer.
POST /account/update_customer
📌 Same route as Step 4
This is the same /account/update_customer route used in Step 4, but with a more complete payload and step_kyc = 4, indicating progress in the KYC flow.
Payload (body)
Response — Success
The person_type field accepts two values: "personal" (individual) or "empresarial" (legal entity).
🚨 Important — this is not the end of registration
Even though the result returns registration_completed, registration is not yet finished. The next required step is starting the liveness process.
8. Starting the Liveness Process
Requests the customer's liveness (facial validation) URL, via integration with Didit.
POST /account/liveness
Payload (body)
Response — Success
🎥 How to run liveness
The URL returned in data.url must be opened using Didit's Web SDK or Mobile SDK (Didit SDK Documentation). If the customer is on a computer, they can scan a QR code and complete liveness on their phone, then return to the flow on the computer — or, if starting registration directly on the phone, complete everything on that device.
NOTE: On desktop, using Didit's SDK, your frontend monitors the completion of liveness and sends the user to the next step, document submission.
9. Identity Document Submission (KYC)
After liveness is completed, the customer submits the image of their identity document.
POST /account/complete_kyc
File (multipart/form-data)
Send the keys "files" for the file and "customer" containing the value with a JSON object holding the account's hash.
Example multipart/form-data:
NOTE:
The document file must be renamed to "document" if it doesn't already have that name.
It must show the PHOTO and CPF (a driver's license, CNH, is preferred) so that Face Match, OCR, and document forensics can be performed to validate the data and complete KYC.
📷 Image quality
The document image must be submitted in good quality — sharp, well-lit, without cropping or glare. Low-quality images increase the chance of KYC rejection (e.g., kyc_data_is_invalid).
10. Final Account / KYC Status Check
Checks the account's final status after submission of the KYC document is complete.
POST /account/kyc_validation
Payload (body)
Response — All good (account approved)
After this call, if the account is complete and validated, the customer receives a confirmation email that their 4Pay account has been opened.
Responses — Account not approved / invalid data
In these cases, the info.result field identifies the specific reason for rejection:
account_not_approved
The account could not be approved at this time.
invalid_data
Data submitted during registration is invalid, in general.
invalid_owner_data
The account holder's data (individual) is invalid.
invalid_company_data
The company's data (legal entity) is invalid.
kyc_data_is_invalid
Data extracted from the KYC process (document/liveness) is invalid.
basic_data_is_invalid
Basic registration data is invalid.
all_data_is_invalid
All submitted data is invalid.
Example rejection response:
Last updated