On this page
- What are Contacts?
- Why Use Contacts?
- Contact Structure
- Required Fields
- Optional Fields
- Creating Contacts
- Basic Contact Creation
- Document Types by Country
- Colombia (CO)
- Brazil (BR)
- Mexico (MX)
- Chile (CL)
- Argentina (AR)
- Peru (PE)
- Bolivia (BO)
- Venezuela (VE)
- United States (US)
- Bank Accounts
- Linking Bank Accounts to Contacts
- Optional Fields with Defaults
- Country-Specific Required Fields
- Retrieving Contacts
- List All Contacts
- Get Specific Contact
- Get Contact’s Bank Accounts
- Updating Contacts
- Validation Requirements
- Person vs Company
- Document Number Formats
- Best Practices
- When to Create Contacts
- Reusing Contacts
- Data Privacy
- Common Scenarios
- Scenario 1: E-Commerce Customer Payment
- Scenario 2: Provider Payout
- Next Steps
Contacts & Bank Accounts
Contacts represent your customers or providers in the Koywe system, storing their information and linked bank accounts for payment operations.
What are Contacts?
A Contact is a person or business entity that you interact with through payments:
- Customers who pay you (PAYIN)
- Providers who you pay (PAYOUT)
- Both in marketplace scenarios
Why Use Contacts?
Track payment history per customer or provider
Store KYC/compliance information (documents, tax IDs)
Link bank accounts for easy recurring payments
Generate reports per contact or business type
Contact Structure
Required Fields
{
"firstName": "Juan", // First name
"countrySymbol": "CO", // Country code
"businessType": "PERSON", // PERSON, COMPANY, etc.
"type": "PERSON" // PERSON, BUSINESS, GOVERNMENT, NGO, FOREIGN
}Optional Fields
{
"lastName": "Pérez", // Last name
"email": "customer@example.com", // Email address
"phone": "+573001234567", // Phone number
"documentType": "CED_CIU", // ID document type (see Document Types by Country). For "BR", a CPF or CNPJ is required: send it here or in documents[]
"documentNumber": "1234567890", // ID document number (required if documentType is provided)
"taxIdType": "NIT", // Tax ID type (deprecated, use documentType)
"taxIdNumber": "9001234561", // Tax ID number (deprecated, use documentNumber)
"addressLine1": "Calle 123", // Street address
"city": "Bogotá", // City
"state": "Cundinamarca", // State / department
"postalCode": "110111" // Postal code
}Creating Contacts
Basic Contact Creation
async function createContact(token, orgId, merchantId, contactData) {
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts`,
{
firstName: contactData.firstName,
lastName: contactData.lastName, // Optional
email: contactData.email, // Optional
phone: contactData.phone, // Optional
countrySymbol: contactData.countrySymbol,
documentType: contactData.documentType, // Optional
documentNumber: contactData.documentNumber, // Optional (required if documentType is provided)
businessType: 'PERSON', // or 'COMPANY'
type: 'PERSON' // PERSON, BUSINESS, GOVERNMENT, NGO, FOREIGN
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
// Usage - Creating a customer contact
const customer = await createContact(token, orgId, merchantId, {
firstName: 'Juan',
lastName: 'Pérez',
countrySymbol: 'CO',
email: 'customer@example.com',
phone: '+573001234567',
documentType: 'CED_CIU',
documentNumber: '1234567890'
});
console.log('Contact created:', customer.id);def create_contact(token, org_id, merchant_id, contact_data):
response = requests.post(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/contacts',
json={
'firstName': contact_data['first_name'],
'lastName': contact_data.get('last_name'), # Optional
'email': contact_data.get('email'), # Optional
'phone': contact_data.get('phone'), # Optional
'countrySymbol': contact_data['country'],
'documentType': contact_data.get('document_type'), # Optional
'documentNumber': contact_data.get('document_number'),# Optional (required if documentType is provided)
'businessType': 'PERSON',
'type': 'PERSON'
},
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
)
return response.json()
# Usage
customer = create_contact(token, org_id, merchant_id, {
'first_name': 'Juan',
'last_name': 'Pérez',
'email': 'customer@example.com',
'phone': '+573001234567',
'country': 'CO',
'document_type': 'CED_CIU',
'document_number': '1234567890'
})curl -X POST 'https://api-sandbox.koywe.com/api/v1/organizations/YOUR_ORG_ID/merchants/YOUR_MERCHANT_ID/contacts' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"firstName": "Juan",
"lastName": "Pérez",
"email": "customer@example.com",
"phone": "+573001234567",
"countrySymbol": "CO",
"documentType": "CED_CIU",
"documentNumber": "1234567890",
"businessType": "PERSON",
"type": "PERSON"
}'Response:
{
"id": "cnt_abc123",
"firstName": "Juan",
"lastName": "Pérez",
"email": "customer@example.com",
"phone": "+573001234567",
"countrySymbol": "CO",
"documentType": "CED_CIU",
"documentNumber": "1234567890",
"businessType": "PERSON",
"createdAt": "2025-11-13T10:00:00Z",
"merchantId": "mrc_xyz789"
}Document Types by Country
Colombia (CO)
Send the code from this table in documentType, not the abbreviation the document goes by in Colombia (CC, CE). If you send CC or CE, the API responds with a 400 error.
| Document Type | Common abbreviation | Code (documentType) | Description | Example |
|---|---|---|---|---|
| Cédula de Ciudadanía | CC | CED_CIU | National ID | 1234567890 |
| Cédula de Extranjería | CE | CED_EXT | Foreign ID | 1234567 |
| Número de Identificación Tributaria | NIT | NIT | Tax ID (companies) | 9001234561 or 900.123.456-1 |
NIT format: accepted as 10 consecutive digits (9001234561) or with dots and a dash (900.123.456-1). Both include the check digit.
The Colombia catalog also includes RC (Registro civil), NOP (foreign NIT), PP (Passport) and DIE (foreign ID document), but they are currently not accepted when creating Colombian contacts: the API responds Document type not supported for the specified country. You can query the full catalog with GET /api/v1/documents?countrySymbol=CO.
Brazil (BR)
| Document Type | Code | Description | Example |
|---|---|---|---|
| CPF | CPF | Individual Tax ID | 123.456.789-09 |
| CNPJ | CNPJ | Company Tax ID (numeric or alphanumeric) | 12.345.678/0001-95 · 12.ABC.345/01DE-35 |
Required document
Contacts with countrySymbol: "BR" must include a CPF (individual) or a CNPJ (company). Brazilian providers reject the payment if the contact has no document, so the API validates it when the contact is created.
- On create: always required. You can send it in
documentType/documentNumberor as an entry indocuments[]. - On update: only validated if the update changes the country or the document. Existing Brazil contacts that have no document can still be edited in other fields.
- The rule uses the contact’s country (
countrySymbol), not their nationality.
If the document is missing, the API responds 400 with code CT00048. See the Error Code Catalog.
Document formats
- CPF: 11 digits, with or without formatting (
123.456.789-09). - CNPJ: 14 characters, with or without formatting (
AA.AAA.AAA/AAAA-DV). The first 12 can be letters or digits and the last 2 (check digits) are always numeric. Since July 2026, the Receita Federal assigns alphanumeric CNPJs to new companies, and numeric CNPJs remain valid. Lowercase letters are accepted and stored as uppercase.- Valid examples:
12.345.678/0001-95,12.ABC.345/01DE-35
- Valid examples:
- The number is stored without formatting. So the same document written with formatting, without it, or in lowercase counts as a duplicate (
CT00023).
Mexico (MX)
| Document Type | Code | Description | Example |
|---|---|---|---|
| RFC | RFC | Tax ID | XAXX010101000 |
| CURP | CURP | Population Registry | ABCD123456HDFXXX09 |
Chile (CL)
| Document Type | Code | Description | Example |
|---|---|---|---|
| RUT | RUT | Tax ID | 11.111.111-1 |
Argentina (AR)
| Document Type | Code | Description | Example |
|---|---|---|---|
| DNI | DNI | National ID | 12345678 |
| CUIT | CUIT | Tax ID | 20-12345678-9 |
Peru (PE)
| Document Type | Code | Description | Example |
|---|---|---|---|
| Documento Nacional de Identidad | DNI | National ID | 12345678 |
| Registro Único de Contribuyentes | RUC | Tax ID (companies) | 20123456789 |
| Carné de Extranjería | CED_EXT | Foreign Resident ID | 001234567 |
Bolivia (BO)
| Document Type | Code | Description | Example |
|---|---|---|---|
| Cédula de Identidad | CED_CIU | National ID | 1234567 |
| Número de Identificación Tributaria | NIT | Tax ID (companies) | 1234567890 |
Venezuela (VE)
| Document Type | Code | Description | Example |
|---|---|---|---|
| Cédula de Identidad | CED_CIU | National ID (V/E prefix) | V12345678 |
| Número de Identificación Tributaria | NIT | Tax ID (companies, J/G prefix) | J123456789 |
United States (US)
| Document Type | Code | Description | Example |
|---|---|---|---|
| Employer Identification Number | EIN | Employer Tax ID | 12-3456789 |
Bank Accounts
Linking Bank Accounts to Contacts
For PAYOUT operations, you need to link a bank account to the contact:
Automatic Association: Bank accounts are automatically linked to the contact’s information. The holder name is taken from the contact’s firstName and lastName (when provided). Bank codes can be deduced from account numbers for most countries.
Optional Fields with Defaults
| Field | Type | Default | Description |
|---|---|---|---|
type | string | VIRTUAL | Account type: SAVINGS, CHECKING, or VIRTUAL. Required for Colombia, except with rail: "BREB". Optional for most countries. |
isVirtual | boolean | false | Whether this is a virtual account. |
isTracked | boolean | false | Whether this account is tracked for balance monitoring. |
rail | string | (none) | Payment rail the destination is reachable through: BANK or BREB. Only meaningful in Colombia — required to store a Bre-B key. See Payment Rails. |
Country-Specific Required Fields
| Country | entity (bankCode) | Notes |
|---|---|---|
| Colombia (CO) | Required | Must provide bank code — except on a Bre-B destination (rail: "BREB"), which has no bank |
| Chile (CL) | Required | Must provide bank code |
| Peru (PE) | Optional | Auto-deduced from account number (CCI) |
| Argentina (AR) | Optional | Auto-deduced from CVU/CBU |
| Mexico (MX) | Optional | Auto-deduced from CLABE |
| Brazil (BR) | Optional | Auto-deduced |
Peru — credit times (PEN and USD): transfers to Banco BCP are instant, 24/7 except 00:00–04:00. Interbank takes about 20 minutes, Monday to Saturday 06:05–21:00. Other banks are credited on a schedule that depends on when the payout is sent, at the latest by the next business day. See Paying Providers for the full schedule and limits.
async function addBankAccountToContact(token, orgId, merchantId, contactId, bankData) {
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts/${contactId}/accounts`,
{
name: bankData.name,
kind: 'BANK',
isDefault: bankData.isDefault,
countrySymbol: bankData.country,
currencySymbol: bankData.currency,
entity: bankData.entity, // Required for CO, CL. Optional for others (auto-deduced)
accountNumber: bankData.accountNumber,
type: bankData.type, // Required for Colombia: 'SAVINGS' or 'CHECKING'
rail: bankData.rail // Optional: 'BANK' or 'BREB'. Required for a Colombian Bre-B key
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
// Usage - Colombia (entity required)
const bankAccountCO = await addBankAccountToContact(token, orgId, merchantId, 'cnt_abc123', {
name: 'Bancolombia COP',
isDefault: true,
country: 'CO',
currency: 'COP',
entity: 'BANCOLOMBIA', // Required for Colombia and Chile
accountNumber: '1234567890',
type: 'SAVINGS' // Required for Colombia
});
// Usage - Peru (entity auto-deduced from CCI)
const bankAccountPE = await addBankAccountToContact(token, orgId, merchantId, 'cnt_def456', {
name: 'Peru PEN',
isDefault: false,
country: 'PE',
currency: 'PEN',
accountNumber: '00219100012345678901' // 20-digit CCI - bank code auto-deduced
});
console.log('Bank account added:', bankAccountCO.id);def add_bank_account_to_contact(token, org_id, merchant_id, contact_id, bank_data):
payload = {
'name': bank_data['name'],
'kind': 'BANK',
'isDefault': bank_data['is_default'],
'countrySymbol': bank_data['country'],
'currencySymbol': bank_data['currency'],
'accountNumber': bank_data['account_number']
}
# entity required for CO, CL; optional for others
if bank_data.get('entity'):
payload['entity'] = bank_data['entity']
# type required for Colombia; optional for other countries (defaults to VIRTUAL)
if bank_data.get('type'):
payload['type'] = bank_data['type']
# rail optional: 'BANK' or 'BREB'. Required for a Colombian Bre-B key
if bank_data.get('rail'):
payload['rail'] = bank_data['rail']
response = requests.post(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/contacts/{contact_id}/accounts',
json=payload,
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
)
return response.json()
# Usage - Colombia (entity required)
bank_account_co = add_bank_account_to_contact(token, org_id, merchant_id, 'cnt_abc123', {
'country': 'CO',
'currency': 'COP',
'entity': 'BANCOLOMBIA', # Required for Colombia and Chile
'account_number': '1234567890',
'type': 'SAVINGS' # Required for Colombia
})
# Usage - Chile (entity required)
bank_account_cl = add_bank_account_to_contact(token, org_id, merchant_id, 'cnt_ghi789', {
'country': 'CL',
'currency': 'CLP',
'entity': 'BANCO_ESTADO', # Required for Chile
'account_number': '12345678901'
})# Colombia - entity required
curl -X POST 'https://api-sandbox.koywe.com/api/v1/organizations/YOUR_ORG_ID/merchants/YOUR_MERCHANT_ID/contacts/cnt_abc123/accounts' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"name": "Bancolombia COP",
"kind": "BANK",
"isDefault": true,
"countrySymbol": "CO",
"currencySymbol": "COP",
"entity": "BANCOLOMBIA",
"accountNumber": "1234567890",
"type": "SAVINGS"
}'Colombia — Bre-B keys: rail is optional, but send rail: "BREB" whenever accountNumber carries a Bre-B key (an email, an @alias, a phone or a document number). Without it the value is validated and settled as a bank account number: usually rejected with BAA00020 / BAA00021, and stored as a bank account when its length happens to match a real one — which pays out over the wrong rail. Declaring it also exempts you from entity and type, which a key does not have. See Payment Rails.
# Colombia — Bre-B key as the destination
curl -X POST 'https://api-sandbox.koywe.com/api/v1/organizations/YOUR_ORG_ID/merchants/YOUR_MERCHANT_ID/contacts/cnt_abc123/accounts' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"name": "Bre-B Juan Pérez",
"kind": "BANK",
"isDefault": true,
"countrySymbol": "CO",
"currencySymbol": "COP",
"accountNumber": "@juanperez",
"rail": "BREB"
}'Country-Specific Requirements: See Merchant External Accounts for detailed validation rules by country. Contact bank accounts follow the same validations (CLABE for Mexico, CCI for Peru, CVU/CBU for Argentina, etc.).
Retrieving Contacts
List All Contacts
async function listContacts(token, orgId, merchantId, options = {}) {
const response = await axios.get(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts`,
{
params: {
search: options.search, // Optional: search by name or email
limit: options.limit || 50,
page: options.page || 1
},
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.data;
}
// Usage
const contacts = await listContacts(token, orgId, merchantId, {
search: 'juan',
limit: 20
});
console.log(`Found ${contacts.length} contacts`);Get Specific Contact
async function getContact(token, orgId, merchantId, contactId) {
const response = await axios.get(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts/${contactId}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.data;
}
// Usage
const contact = await getContact(token, orgId, merchantId, 'cnt_abc123');
console.log('Contact:', contact);Get Contact’s Bank Accounts
async function getContactBankAccounts(token, orgId, merchantId, contactId) {
const response = await axios.get(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts/${contactId}/accounts`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.data;
}
// Usage
const bankAccounts = await getContactBankAccounts(token, orgId, merchantId, 'cnt_abc123');
console.log('Bank accounts:', bankAccounts);Updating Contacts
async function updateContact(token, orgId, merchantId, contactId, updates) {
const response = await axios.put(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/contacts/${contactId}`,
updates,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
// Usage - Update phone number or address
const updated = await updateContact(token, orgId, merchantId, 'cnt_abc123', {
phone: '+573009876543',
addressLine1: 'Calle 456',
city: 'Medellín'
});Validation Requirements
Person vs Company
For PERSON (individual):
- Use individual document types (CED_CIU, DNI, CPF, etc.)
businessType:"PERSON"- Individual’s
firstName(and optionallylastName)
For COMPANY (business):
- Use company document types (NIT, CNPJ, CUIT, etc.)
businessType:"COMPANY"type:"BUSINESS"- Company legal name as
firstName
Document Number Formats
Document numbers must match the expected format for each country:
- Colombia CED_CIU / CED_EXT: digits only (up to 15)
- Colombia NIT: 10 digits (
9001234561) or with dots and dash (900.123.456-1) - Brazil CPF: 11 digits (with or without formatting)
- Brazil CNPJ: 14 characters, numeric or alphanumeric (with or without formatting)
- Mexico RFC: 12-13 characters
- Chile RUT: Format XX.XXX.XXX-X
Best Practices
When to Create Contacts
Create contacts for:
- Customers making payments (PAYIN) - for tracking and compliance
- Providers receiving payments (PAYOUT) - required for bank account linking
- Recurring transactions with the same entity
Reusing Contacts
Reuse existing contacts for the same customer/provider to:
- Maintain payment history
- Avoid duplicate records
- Simplify reconciliation
// Check if contact exists before creating
async function getOrCreateContact(token, orgId, merchantId, email) {
// Try to find existing
const contacts = await listContacts(token, orgId, merchantId, { search: email });
if (contacts.length > 0) {
return contacts[0]; // Return existing
}
// Create new if not found
return await createContact(token, orgId, merchantId, {
email: email,
// ... other fields
});
}Data Privacy
Security Considerations:
- Store only necessary information
- Follow GDPR/data protection regulations
- Don’t store sensitive data in metadata fields
- Implement proper access controls
Common Scenarios
Scenario 1: E-Commerce Customer Payment
// 1. Create customer contact
const customer = await createContact(token, orgId, merchantId, {
firstName: 'María',
lastName: 'García',
countrySymbol: 'CO',
businessType: 'PERSON',
type: 'PERSON',
email: 'customer@example.com',
documentType: 'CED_CIU',
documentNumber: '1234567890',
phone: '+573001234567'
});
// 2. Create PAYIN order linked to contact
const order = await createPayinOrder(token, orgId, merchantId, {
contactId: customer.id,
amount: 50000,
currency: 'COP',
// ... other fields
});Scenario 2: Provider Payout
// 1. Create provider contact
const provider = await createContact(token, orgId, merchantId, {
firstName: 'Servicios ABC SAS',
countrySymbol: 'CO',
businessType: 'COMPANY',
type: 'BUSINESS',
email: 'provider@example.com',
documentType: 'NIT',
documentNumber: '9001234561'
});
// 2. Add provider's bank account
const bankAccount = await addBankAccountToContact(token, orgId, merchantId, provider.id, {
name: 'Bancolombia COP',
kind: 'BANK',
isDefault: true,
countrySymbol: 'CO',
currencySymbol: 'COP',
entity: 'BANCOLOMBIA',
accountNumber: '1234567890',
type: 'CHECKING'
});
// 3. Create PAYOUT order
const payout = await createPayoutOrder(token, orgId, merchantId, {
contactId: provider.id,
destinationAccountId: bankAccount.id, // Link to bank account
amount: 500000,
currency: 'COP'
});