Import Companies, Contacts and Customers
If you have an external system like an ERP or CRM that contains your company and contact data, you can import this data into Propeller using the bulk REST endpoints. In a B2B scenario you typically import companies and their contacts. In a B2C scenario you import customers. The bulk endpoints use upsert operations to either create new records or update existing ones, so you can use the same integration for your initial import and for ongoing syncs.
Endpoints
POST /v2/companies/bulk/sourceId
POST /v2/contacts/bulk/sourceId
POST /v2/customers/bulk/sourceId
You can also use id instead of sourceId as the lookup key, but this requires the external system to know the Propeller internal IDs. The sourceId variant is the recommended choice.
Import order
Companies must be imported before contacts. A contact belongs to a company, and the link between them is established through the parent field on the contact. If the parent company does not exist yet in Propeller when the contact is imported, the response will return an error and the contact will not be created.
Customers are independent and do not have a parent. They can be imported at any time.
Request body: companies
{
"companies": [
{
"sources": [{ "name": "MY_ERP", "id": "1032644" }],
"language": "NL",
"name": "Bakker Bouw B.V.",
"debtorId": "29931",
"taxNumber": "NL812345678B01",
"cocNumber": "12345678",
"email": "info@bakkerbouw.nl",
"phone": "0301234567",
"addresses": [
{
"type": "invoice",
"code": "34766",
"street": "Kerkstraat",
"number": "34",
"postalCode": "3511 AB",
"city": "Utrecht",
"country": "NL",
"email": "factuur@bakkerbouw.nl",
"isDefault": "Y"
}
]
},
{
"sources": [{ "name": "MY_ERP", "id": "1032645" }],
"language": "NL",
"name": "De Vries Houthandel B.V.",
"debtorId": "29932",
"taxNumber": "NL876543210B01",
"cocNumber": "87654321",
"email": "info@devrieshout.nl",
"phone": "0207654321"
}
],
"directives": [
{
"operation": "skipIfNotEmpty",
"fields": ["name", "email"]
}
]
}
The request includes:
companies[]contains the companies to create or updatesourcesidentifies each company in the external system (see Source identification below)addresses[]can be included inline to create or update company addresses (see Attributes and addresses below)directives[]controls how updates are handled (see Directives below)
Request body: contacts
{
"contacts": [
{
"sources": [{ "name": "MY_ERP", "id": "69573" }],
"language": "NL",
"parent": {
"source": "MY_ERP",
"sourceId": "1032644"
},
"firstName": "Jan",
"middleName": "van der",
"lastName": "Berg",
"debtorId": "29947",
"email": "j.vanderberg@bakkerbouw.nl",
"phone": "0301234568",
"mobile": "0612345678",
"primaryLanguage": "NL",
"gender": "M"
},
{
"sources": [{ "name": "MY_ERP", "id": "69574" }],
"language": "NL",
"parent": {
"source": "MY_ERP",
"sourceId": "1032644"
},
"firstName": "Lisa",
"lastName": "Jansen",
"debtorId": "29948",
"email": "l.jansen@bakkerbouw.nl",
"phone": "0301234569",
"primaryLanguage": "NL",
"gender": "F"
}
],
"directives": [
{
"operation": "skipIfNotEmpty",
"fields": ["firstName", "lastName", "email"]
}
]
}
The contact payload includes:
parentlinks the contact to a company using the company's source name and source ID (see Linking contacts to companies below)- Person fields like
firstName,middleName,lastName,email,phone,mobile,genderandprimaryLanguage
Request body: customers
Customers use the same person fields as contacts (firstName, lastName, email, etc.) but do not have a parent company. The parent field is deprecated on customers. Customers can include inline addresses.
{
"customers": [
{
"sources": [{ "name": "MY_ERP", "id": "1032656" }],
"language": "NL",
"firstName": "Pieter",
"middleName": "",
"lastName": "Alberts",
"debtorId": "29935",
"phone": "0612349876",
"primaryLanguage": "NL",
"addresses": [
{
"type": "invoice",
"code": "34772",
"street": "Dorpsweg",
"number": "60",
"postalCode": "9138 TH",
"city": "Oosterbeek",
"country": "NL",
"isDefault": "Y"
}
]
}
]
}
Source identification
The sources array identifies each record in the external system. Each source entry has a name (the name of the external system, for example "MY_ERP") and an id (the unique identifier of the record in that system). The recommendation is the source name to be uppercase with underscores and use the same one across all integrations with the same system. The source ID should be whatever unique identifier the external system uses for the record.
This combination allows the bulk endpoint to determine whether a record already exists in Propeller. If a record with that source combination exists, it gets updated. If not, a new record is created. See Sources for more on how source combinations work.
Linking contacts to companies
To assign a contact to a company, fill the parent field with the source and sourceId of the parent company. This way you do not need to know the Propeller internal company ID. As long as the company was imported with the same source name and source ID, Propeller resolves the link automatically.
"parent": {
"source": "MY_ERP",
"sourceId": "1032644"
}
This is why companies must be imported before contacts. The parent company must already exist in Propeller for the link to be established.
Fields and validation
You can find the full list of available fields in the REST API Reference. The endpoints enforce validation rules on several fields. If a record fails validation, the endpoint returns an error for that specific record and stops processing the whole batch. Validating your data before sending it to Propeller helps avoid partial failures.
Key validation rules:
emailmust be a valid email formatname(company): max 200 charactersfirstName: max 30 characters (contacts), max 100 characters (customers)middleName: max 20 characterslastName: max 50 characters (contacts), max 100 characters (customers)phoneandmobile: max 30 characterstaxNumber: max 50 characterscocNumber: max 50 characters
Attributes and addresses
You can include attributes and addresses as sub-objects in the same payload.
Attributes can be sent with companies, contacts and customers. Make sure the attribute descriptions already exist in Propeller before sending attribute values.
Addresses can be included in the company and customer payloads. Each address requires a type (delivery, invoice or home), street, postalCode, city and country. Contacts do not support addresses in the bulk payload. To import addresses for companies separately, see Import Addresses.
Directives
Directives are optional but can be useful when the external system and Propeller each own different parts of the data.
skipIfNotEmpty: use this when the external system provides the initial data but you want to enrich specific fields in Propeller. Specify the fields you want to protect (for example name and email). On future updates, those fields will not be overwritten if they already have content in Propeller.
skipCreate: use this when you only want to update existing records and never create new ones. Records that do not already exist in Propeller will be skipped.
See Directives for the full list of available directives.
Batch size
The bulk endpoints accept up to 1000 records per request, but we recommend sending 100 to 200 records per batch. Smaller batches are easier to debug when something goes wrong. See Sync Strategies for more on batch sizing and scheduling.
Response
Each record in the batch returns a result:
{
"data": [
{
"action": "create",
"id": 123,
"success": true,
"lookupKey": "sourceId",
"key": "1032644"
}
],
"messages": ["Bulk operation completed successfully"],
"total": 1
}
successindicates whether the operation succeededkeyis the source ID of the recordidis the Propeller internal IDactioniscreate,updateorskipdepending on the outcomelookupKeyconfirms the identifier type used
See also
- Sources for how source combinations work in bulk operations
- Directives for all available directive types
- Sync Strategies for scheduling and batch sizing recommendations
- Import Addresses for importing addresses separately
- REST API Reference for the full endpoint specification