Create a customer (V2 API)
V3 API Available (Beta)
A new V3 Customer API is available in beta. Key differences:
- Simpler workflow: Primary address included in customer creation (no separate request needed)
- More address fields: GLN, department, delivery terms (Incoterms)
- Better filtering:
namefield now supportscontains,startsWith,endsWithImportant:
- V3 requires feature flag activation (
api-v3-customers) - contact [email protected]- V3 is still in beta - minor changes may occur before stable release
- Existing V2 integrations will continue to work
See Create Customer (V3) for the new API.
Contents
- Overview
- ERP Context
- At a Glance
- Customer Number Assignment
- Prerequisites
- Before You Start
- Workflow
- Step-by-Step Guide
- Address Handling
- Financial Settings
- Preventing Duplicates
- Error Handling
- Related Resources
Overview
This guide shows you how to create a customer via the Xentral API. You'll learn the difference between person and company customers, how to add addresses, and how to configure financial settings.
Use Cases:
- Import customers from an online shop (Shopify, WooCommerce, etc.) into Xentral
- Synchronize customer data from a CRM system
- Create B2B customers from a partner portal
- Migrate customers from another ERP system
New to the Xentral API? Read first:
- Authentication - Create API Token
- Rate Limiting - Request limits
ERP Context
Important for developers without ERP experience: A customer in Xentral is essentially an address with the "customer" role assigned to it. An address can have different roles (customer, supplier, employee), but each role is managed via separate API endpoints.
What is linked to a customer?
In Xentral, a customer record is connected to:
- Sales Orders - Orders placed by this customer
- Invoices - Billing documents and open items
- Sales Prices - Customer-specific pricing (can also be linked to customer groups)
- Customer Groups - For pricing and categorization
- Offers - Quotations for this customer
- Delivery Notes - Shipping documents
- Credit Notes - Refunds and corrections
- Return Orders - Returns processing
- Proforma Invoices - Pre-invoices
- Productions - Manufacturing orders
What is configured ON a customer?
These are settings stored on the customer record (not separate linked entities):
- Payment method and payment terms
- Tax settings (VAT ID, taxation type)
- Document delivery preferences (email vs. postal)
- Shipping method defaults
- Bank account information (for SEPA)
Why is this important?
Understanding this helps you:
- Know which data must be present (customer type, name)
- Know which data should be added (payment method, tax info, shipping, custom fields)
- Understand that addresses defined on the customer serve as defaults - you can override them on individual documents
At a Glance
| Endpoint | POST /api/v2/customers |
| Method | POST |
| Auth | Bearer Token (Documentation) |
| Required Scope | customer:create |
Minimal example (Person):
curl -X POST "https://{instance}.xentral.biz/api/v2/customers" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"customerType": "person",
"firstname": "Max",
"lastname": "Mustermann"
}'Minimal example (Company):
curl -X POST "https://{instance}.xentral.biz/api/v2/customers" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"customerType": "company",
"name": "ACME GmbH"
}'Response: 201 Created - The new customer ID is in the Location header (e.g., /api/v2/customers/11).
→ Full API Reference: Create Customer
Customer Number Assignment
Automatic Assignment (Default)
If you don't provide a number field, Xentral automatically assigns the next available customer number from the configured number range:
| Scenario | Number Range Used |
|---|---|
| No project specified | System-wide number range |
| Project without custom ranges | System-wide number range |
| Project with custom ranges enabled | Project's own number range |
Tip: Check your number range configuration in Xentral: Settings > General Settings > Number ranges
Providing Custom Numbers (Data Migration)
For data migration or legacy imports, you can provide your own customer number:
curl -X POST "https://{instance}.xentral.biz/api/v2/customers" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"customerType": "company",
"name": "Migrated Customer GmbH",
"number": "12345"
}'
Warning: Custom numbers must be unique. If the number already exists, you'll get a409 Conflicterror:{"title":"Customer already exists.","messages":["Customer number \"12345\" already exists in project 0"]}
Best Practices for Number Ranges
| Scenario | Recommendation |
|---|---|
| New integration | Let Xentral assign numbers automatically |
| Data migration | Provide existing numbers, ensure no gaps/duplicates |
| DATEV users | Use numeric-only numbers, same digit count (5-6 digits) |
| High volume B2C | Consider project-specific number ranges |
DATEV Compatibility
If you export to DATEV (German accounting software), follow these rules:
- No letters in customer numbers (only digits)
- Same digit count for all customers and suppliers
- SKR04 example: Customers 10000-69999, Suppliers 70000-99999
- Never use
deviatingCustomerNumberfor shop references (reserved for DATEV!)
Reference: Nummernkreise verwenden
Prerequisites
- Xentral account with API access
- Personal Access Token (PAT) with scope
customer:create(Guide)
Before You Start
Decision 1: Person or Company?
The customerType determines which fields are required:
| Customer Type | Required Fields | Use Case |
|---|---|---|
person | firstname, lastname | B2C customers, individuals |
company | name | B2B customers, businesses |
Note: You can change the customer type later via PATCH request or in the UI, but it's best to choose correctly from the start.
Decision 2: Add addresses now or later?
Addresses cannot be included when creating the customer. You must add them in separate requests after the customer is created.
Option A: Minimal approach
- Create customer with basic data only
- Define addresses directly on documents (sales orders) when needed
- Good for: Quick imports, data migration, one-time customers
Option B: Complete approach
- Create customer with payment method, tax settings, shipping defaults
- Add masterdata address (becomes default for billing AND delivery)
- Optionally add separate billing/delivery addresses if they differ
- Good for: B2B customers, regular buyers
Note: If no billing or delivery address is defined, the masterdata address is used as both billing and delivery address. Separate addresses are only needed when they differ from the masterdata address.
Decision 3: Store external reference?
If you're syncing customers from an external system (shop, CRM), you need a way to link them:
Options:
- Use a custom field (
customFields.free1tofree20) for your shop's customer ID - Store the mapping in your middleware/database
Warning: Do NOT usedeviatingCustomerNumberfor shop references! This field is exclusively for DATEV export (German accounting software) and using it for other purposes can cause serious accounting issues.
Best Practice: Always store a mapping between your external customer ID and the Xentral customer ID. Use custom fields or your own database for this mapping.
Workflow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1. Check │ ──▶ │ 2. Create │ ──▶ │ 3. Add │ ──▶ │ 4. Verify │
│ Existing? │ │ Customer │ │ Addresses │ │ Customer │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Found? │ │ Store ID │
│ Use ID! │ │ Mapping │
└─────────────┘ └─────────────┘
Step-by-Step Guide
Step 1: Check if Customer Exists
Before creating a new customer, check if they already exist to avoid duplicates.
Search by email (V2 API):
Required Scope:
customer:read
curl -s "https://{instance}.xentral.biz/api/v2/customers?filter[0][key]=email&filter[0][op]=equals&filter[0][value][email protected]" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"→ API Reference: List Customers
Response:
{
"data": [],
"extra": {"totalCount": 0, "page": {"number": 1, "size": 10}}
}If totalCount > 0, the customer already exists. Use the existing ID instead of creating a new one.
Search alternatives:
- By name:
filter[0][key]=name&filter[0][op]=equals&filter[0][value]=Max%20Mustermann - By customer number:
filter[0][key]=number&filter[0][op]=equals&filter[0][value]=10001 - By address:
filter[0][key]=city&filter[0][op]=equals&filter[0][value]=Berlin
Combined filters (AND logic):
# Search by name AND email (more precise matching)
curl -s "https://{instance}.xentral.biz/api/v2/customers?filter[0][key]=name&filter[0][op]=equals&filter[0][value]=Max%20Mustermann&filter[1][key]=email&filter[1][op]=equals&filter[1][value][email protected]" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"Note: In V2, the
namefield only supports theequalsoperator. ForcontainsandstartsWith, use the V3 API.
Step 2: Create Customer
Create a person (B2C):
Required Scope:
customer:create
curl -X POST "https://{instance}.xentral.biz/api/v2/customers" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"customerType": "person",
"firstname": "Max",
"lastname": "Mustermann",
"contactDetails": {
"email": "[email protected]",
"phone": "+49 30 123456"
}
}'→ API Reference: Create Customer
Response: 201 Created
The customer ID is in the Location header:
Location: https://{instance}.xentral.biz/api/v2/customers/11
Create a company (B2B):
curl -X POST "https://{instance}.xentral.biz/api/v2/customers" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"customerType": "company",
"name": "ACME GmbH",
"contactDetails": {
"email": "[email protected]",
"phone": "+49 89 987654"
}
}'Note: If you don't specify a
number, Xentral automatically assigns the next available customer number from the configured number range.
Important Optional Parameters
While only customerType and name fields are required, these parameters are important for business processes:
| Parameter | Purpose | When to use |
|---|---|---|
project | Assign to a project/mandant | Multi-project setups |
groups | Customer groups (for pricing) | B2B with special prices |
customFields | Free fields 1-20 | Store external references |
financials.tax | Taxation, VAT ID | B2B, EU customers |
financials.paymentMethod | Default payment method | All customers |
financials.paymentTerms | Payment deadline, discount | Payment method "Invoice" |
documentDelivery | Auto-create documents | Automated workflows |
shipping | Default shipping method | Recurring customers |
→ API Reference: Create Customer for all available fields
Step 3: Add Masterdata Address
The masterdata address is the customer's primary address. If no separate billing or delivery address is defined, this address is used for both.
Required Scope:
customerAddr:create
curl -X POST "https://{instance}.xentral.biz/api/v2/customers/{customerId}/addresses" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"type": "masterdata",
"street": "Hauptstraße 1",
"zip": "10115",
"city": "Berlin",
"country": "DE"
}'→ API Reference: Create Customer Address
Response: 201 Created
Location: https://{instance}.xentral.biz/api/v2/customers/11/addresses/masterdata
Note: Each customer has exactly one masterdata address. It is accessed via the URL path
/addresses/masterdata(not a numeric ID).
Step 4: Add Delivery Address (optional)
Add a separate delivery address only if it differs from the masterdata address.
Note: Only ONE delivery address can have
defaultDeliveryAddress: true. Setting this flag on a new address automatically removes it from the previous default.Required Scope:
customerAddr:create
curl -X POST "https://{instance}.xentral.biz/api/v2/customers/{customerId}/addresses" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"type": "deliveryaddress",
"name": "Max Mustermann",
"street": "Lieferstraße 42",
"zip": "10115",
"city": "Berlin",
"country": "DE",
"deliveryDetails": {
"defaultDeliveryAddress": true,
"taxType": "domestic"
}
}'→ API Reference: Create Customer Address
Response: 201 Created
Location: https://{instance}.xentral.biz/api/v2/customers/11/addresses/2
Important: When usingdeliveryDetails, thetaxTypefield is required. Allowed values:domestic,eu-delivery,export,free.
Step 5: Add Billing Address (optional)
Add a separate billing address only if it differs from the masterdata address.
Required Scope:
customerAddr:create
curl -X POST "https://{instance}.xentral.biz/api/v2/customers/{customerId}/addresses" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"type": "billingaddress",
"name": "ACME GmbH - Buchhaltung",
"street": "Finanzstraße 99",
"zip": "80331",
"city": "München",
"country": "DE"
}'Note: Each customer has exactly one billing address. If not defined, the masterdata address is used for billing.
Step 6: Verify Customer (optional)
Required Scope:
customer:read
curl -s "https://{instance}.xentral.biz/api/v2/customers/{customerId}" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"→ API Reference: View Customer
Response:
{
"data": {
"id": "11",
"number": "10005",
"customerType": "person",
"name": "Max Mustermann",
"firstname": "Max",
"lastname": "Mustermann",
"contactDetails": {
"email": "[email protected]",
"phone": "+49 30 123456"
},
"createdAt": "2026-02-02T14:06:09Z",
"updatedAt": "2026-02-02T14:06:09Z"
}
}List addresses:
Required Scope:
customerAddr:read
curl -s "https://{instance}.xentral.biz/api/v2/customers/{customerId}/addresses" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"→ API Reference: List Customer Addresses
Response:
{
"data": [
{
"id": "masterdata",
"type": "masterdata",
"street": "Hauptstraße 1",
"zip": "10115",
"city": "Berlin",
"country": "DE"
},
{
"id": "billingaddress",
"type": "billingaddress",
"name": "ACME GmbH - Buchhaltung",
"street": "Finanzstraße 99",
"zip": "80331",
"city": "München",
"country": "DE"
},
{
"id": "2",
"type": "deliveryaddress",
"name": "Max Mustermann",
"street": "Lieferstraße 42",
"zip": "10115",
"city": "Berlin",
"country": "DE",
"deliveryDetails": {
"defaultDeliveryAddress": true,
"taxType": "domestic"
}
}
],
"extra": {
"totalCount": 3,
"page": {"number": 1, "size": 10}
}
}Update Customer (PATCH)
To update customer data after creation:
Required Scope:
customer:update
curl -X PATCH "https://{instance}.xentral.biz/api/v2/customers/{customerId}" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"contactDetails": {"email": "[email protected]"}}'→ API Reference: Update Customer
Response: 204 No Content - The update was successful. No body is returned.
Address Handling
Xentral uses three address types:
| Type | Description | Count | ID Format |
|---|---|---|---|
masterdata | Primary/main address | 1 per customer | "masterdata" (URL path) |
billingaddress | Invoice address (optional) | 0-1 per customer | "billingaddress" (URL path) |
deliveryaddress | Shipping address (optional) | 0-n per customer | Numeric (e.g., "2") |
Important: If no billing or delivery address is defined, the masterdata address is used for both. You only need separate addresses when they differ from the masterdata address.
→ API Reference: Create Customer Address for required fields per type
UPSERT Behavior for masterdata and billingaddress
Important: POST requests tomasterdataorbillingaddressbehave as UPSERT operations. If an address of that type already exists, it will be overwritten, not rejected.
This means:
- Creating a second
masterdataaddress replaces the existing one - Creating a second
billingaddressaddress replaces the existing one - No error is returned - you receive
201 Created - Only
deliveryaddressallows creating multiple addresses (each gets a numeric ID)
The Default Delivery Address Behavior
Understanding how delivery addresses work:
Scenario 1: No delivery address defined
- The masterdata address is used as delivery address
- This works fine for most cases
Scenario 2: Multiple delivery addresses, none set as default
- The sales order uses the masterdata address (not any of the delivery addresses)
- The separate delivery addresses are available but not auto-selected
Scenario 3: One delivery address with defaultDeliveryAddress: true
- This address is automatically used for new sales orders
- Only ONE address can have this flag - setting it on a new address removes it from the previous one
Best Practice: If you add separate delivery addresses, mark one as default:
{
"deliveryDetails": {
"defaultDeliveryAddress": true,
"taxType": "domestic"
}
}Financial Settings
When creating a customer, you can optionally set financial parameters. These settings are used as defaults when creating documents (orders, invoices) for this customer.
curl -X POST "https://{instance}.xentral.biz/api/v2/customers" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"customerType": "company",
"name": "ACME GmbH",
"financials": {
"tax": {
"taxation": "domestic",
"vatId": "DE123456789"
},
"paymentTerms": {
"paymentTargetDays": 14,
"paymentTargetDiscount": 2.0,
"paymentTargetDiscountDays": 7
},
"paymentMethod": {"id": "3"},
"creditLimit": 5000
}
}'Payment Method
To set a default payment method, first retrieve available methods:
curl -s "https://{instance}.xentral.biz/api/v2/paymentMethods" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"→ API Reference: List Payment Methods
Then use the ID in financials.paymentMethod.id.
Tax Settings
| Field | Values | Description |
|---|---|---|
taxation | domestic, eu-delivery, export, free | Tax treatment |
vatId | e.g., "DE123456789" | VAT identification number |
taxNumber | e.g., "12/345/67890" | Tax number |
Payment Terms
Note: Payment terms are only relevant when using payment method "Invoice" (Rechnung).
| Field | Type | Description |
|---|---|---|
paymentTargetDays | integer | Days until payment is due |
paymentTargetDiscount | float | Discount percentage for early payment |
paymentTargetDiscountDays | integer | Days to qualify for discount |
Preventing Duplicates
See Step 1: Check if Customer Exists for how to search before creating.
Best Practices
| Strategy | Recommendation |
|---|---|
| Check first | Always search by email AND/OR name before creating |
| Store mapping | Keep external ID → Xentral ID in your own database |
| Use custom fields | Store shop customer ID in customFields.free1 (NOT in deviatingCustomerNumber!) |
Reminder: Do NOT usedeviatingCustomerNumberfor shop references - this field is reserved for DATEV export.
Error Handling
| Status | Meaning | Common Cause |
|---|---|---|
| 400 | Bad Request | Validation error (missing required fields, invalid format) |
| 401 | Unauthorized | Invalid or expired token |
| 403 | Forbidden | Missing required scope (e.g., customer:create) |
| 404 | Not Found | Customer ID doesn't exist (when adding addresses) |
| 406 | Not Acceptable | Missing Accept: application/json header |
| 409 | Conflict | Customer number already exists |
| 429 | Too Many Requests | Rate limit exceeded |
Note: Validation errors return
400 Bad Request, not422.
Common Validation Errors
Missing required fields (person):
{
"messages": {
"firstname": ["The firstname field is required when customer type is person."],
"lastname": ["The lastname field is required when customer type is person."]
}
}Missing taxType in deliveryDetails:
{
"messages": {
"deliveryDetails.taxType": ["delivery details.tax type must be filled in if delivery details is filled in."]
}
}Rate Limiting Best Practice
Monitor the X-RateLimit-Remaining header to avoid hitting limits:
| Remaining | Recommended Action |
|---|---|
| 100-50 | Full speed (no delay) |
| 50-25 | Add a small delay (e.g., 50ms) between requests |
| < 25 | Add a larger delay (e.g., 200ms) between requests |
Related Resources
API Documentation
- customercreatev2 - Create Customer
- customerlistv2 - List Customers
- customerviewv2 - View Customer
- customerupdatev2 - Update Customer
- customeraddresscreatev2 - Create Address
- customeraddresslistv2 - List Addresses
- Authentication
- Rate Limiting
Related Guides
- G1: Create Sales Order - Create orders for customers
Updated about 5 hours ago