Create several customers of a company
Scopecustomers:writeCreates up to 500 customers of this company (NIF) in a single call.
- Atomic: if any customer fails validation the whole batch is rejected with
422BULK_VALIDATION_ERRORand nothing is persisted. This is not a partial operation. dry_run: withdry_run=truethe batch is only validated — tax identifiers against the AEAT register, duplicates inside the batch and against the existing customers, field formats — nothing is written and the answer is200. Withdry_run=false, the default, validation is followed by creation and the answer is201.- Report: both modes return the same per-record report, so a dry run and a real run are read the same way.
Keys are prefixed beel_sk_, and each one carries the scopes it was created with: a key
short of the scope an operation needs is answered 403. The scope an operation requires
is shown next to its title, and the full catalogue lives in the Scopes reference.
Keys are created from the BeeL dashboard. They are secret credentials: do not share them or commit them to source control.
In: header
Path Parameters
NIF (company) the operation acts on. It is the only source of context: the account that owns it is derived from it, and the BeeL-Active-Company header plays no part. A NIF you do not reach answers 403, and so does a NIF that does not exist, so the existence of a NIF in another account is never disclosed.
uuidQuery Parameters
Validate the batch without persisting it (true), or validate and create it (false,
the default). Either way the batch is atomic.
falseHeader Parameters
Idempotency key to prevent duplicates in sensitive operations.
- Any unique client-generated string (e.g. an order id). A UUID also works but is not required
- Allowed characters: letters, digits,
_and-(max 255 chars) - If the same key is sent twice, the result of the first operation is returned
- Keys expire 24 hours after processing
The key is scoped per user and environment, and bound to the request body, so retrying after a network timeout replays the stored response instead of repeating the operation.
| Status | Code | When |
|---|---|---|
400 | INVALID_IDEMPOTENCY_KEY | The key breaks the format rules above. |
409 | IDEMPOTENCY_KEY_PROCESSING | The first request is still in flight. Wait and retry with the same key. |
409 | IDEMPOTENCY_KEY_MISMATCH | The key was already used with a different body. Use a new key. |
^[a-zA-Z0-9_-]+$length <= 2551 <= items <= 500Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://app.beel.es/api/v1/companies/497f6eca-6276-4993-bfeb-53cbbbba6f08/customers/bulk" \ -H "Content-Type: application/json" \ -d '{ "customers": [ { "legal_name": "Construcciones García SL", "nif": "B11111111", "email": "info@construccionesgarcia.com", "address": { "street": "Calle Albañil", "number": "15", "postal_code": "28010", "city": "Madrid", "province": "Madrid", "country": "España" } }, { "legal_name": "Pedro Martínez Ruiz", "nif": "11111111A", "email": "pedro.martinez@email.com", "address": { "street": "Avenida Principal", "number": "200", "postal_code": "08015", "city": "Barcelona", "province": "Barcelona", "country": "España" } }, { "legal_name": "Distribuidora Levante SA", "nif": "A22222222", "email": "contabilidad@distribuidoralevante.es", "address": { "street": "Polígono Industrial", "number": "Nave 7", "postal_code": "46015", "city": "Valencia", "province": "Valencia", "country": "España" } } ] }'All-or-nothing: the batch is only persisted when every row can be imported, so statistics.imported either matches total_processed or is 0. Each row is still reported individually, with the customer_id of what it created.
{
"success": true,
"data": {
"metadata": {
"total_customers": 3,
"is_dry_run": false,
"processing_time_ms": 180,
"source_type": "BULK_JSON"
},
"customers_validation": [
{
"index": 0,
"status": "VALID",
"customer_id": "bbb222cc-33dd-44ee-5566-77ff88990011",
"customer": {
"legal_name": "Construcciones García SL",
"nif": "B11111111"
},
"errors": [],
"warnings": []
},
{
"index": 1,
"status": "VALID",
"customer_id": "ccc333dd-44ee-55ff-6677-88990011aabb",
"customer": {
"legal_name": "Pedro Martínez Ruiz",
"nif": "11111111A"
},
"errors": [],
"warnings": []
},
{
"index": 2,
"status": "VALID",
"customer_id": "ddd444ee-55ff-66aa-7788-990011aabbcc",
"customer": {
"legal_name": "Distribuidora Levante SA",
"nif": "A22222222"
},
"errors": [],
"warnings": []
}
],
"statistics": {
"total_processed": 3,
"valid": 3,
"with_warnings": 0,
"with_errors": 0,
"duplicates": 0,
"invalid_nifs": 0,
"importable": 3,
"not_importable": 0,
"imported": 3,
"success_rate": 1
}
},
"meta": {
"timestamp": "2025-02-08T10:00:00Z",
"request_id": "req_bulk_001"
}
}{
"success": true,
"data": {
"metadata": {
"total_customers": 10,
"is_dry_run": true,
"processing_time_ms": 450,
"source_type": "CSV_IMPORT",
"filename": "clientes_enero.csv",
"file_size_bytes": 1048576,
"total_rows": 250
},
"customers_validation": [
{
"index": 0,
"customer": {
"legal_name": "Empresa Cliente SL",
"trade_name": "EmpresaCliente",
"nif": "12345678A",
"alternative_id": {
"type": "NIF_IVA",
"number": "string",
"country_code": "st"
},
"address": {
"street": "Calle Mayor, 123",
"number": "123",
"floor": "2º A",
"door": "A",
"postal_code": "28001",
"city": "Madrid",
"province": "Madrid",
"country": "España",
"country_code": "ES"
},
"phone": "+34 612 345 678",
"email": "user@example.com",
"website": "string",
"billing_emails": [
"user@example.com"
],
"contact_person": "María García",
"notes": "string",
"preferred_payment_method": {
"method": "BANK_TRANSFER",
"iban": "ES1234567890123456789012",
"swift": "ABCDESMMXXX",
"payment_term_days": 30
},
"general_discount": 100,
"active": true
},
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "VALID",
"errors": [
{
"field": "nif",
"value": "12345678X",
"message": "NIF not found in AEAT census",
"code": "CUSTOMER_IDENTIFIER_REQUIRED"
}
],
"warnings": [
{
"field": "email",
"message": "Email not provided, NIF will be used for invoicing"
}
],
"row_number": 15
}
],
"statistics": {
"total_processed": 10,
"valid": 6,
"with_warnings": 2,
"with_errors": 2,
"duplicates": 1,
"invalid_nifs": 1,
"imported": 8,
"success_rate": 0.8,
"importable": 8,
"not_importable": 2
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "BAD_REQUEST",
"message": "Invalid request"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication is required to access this resource"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The provided data is not valid",
"details": {
"field": "specific error message"
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
},
"type": "https://docs.beel.es/errors/INVOICE_NO_LINES",
"title": "INVOICE_NO_LINES",
"detail": "La factura debe tener al menos una línea",
"instance": "/v1/invoices/abc-123"
}{
"success": false,
"error": {
"code": "CONCURRENT_MODIFICATION",
"message": "The resource was modified by another request; read it again and retry"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The provided data is not valid",
"details": {
"field": "specific error message"
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
},
"type": "https://docs.beel.es/errors/INVOICE_NO_LINES",
"title": "INVOICE_NO_LINES",
"detail": "La factura debe tener al menos una línea",
"instance": "/v1/invoices/abc-123"
}{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again in 60 seconds."
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "UNSUPPORTED_MEDIA_TYPE",
"message": "Unsupported media type: text/plain. Supported: application/json"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}Preview an import of customers into a company POST
Parses and validates the file without writing anything: no customer is created. It returns the same per-record outcome the import would produce, so the caller can correct the data before importing it with `POST .../customers/imports`. - **`source`:** the origin travels in the body, exactly as in the import.
Import customers into a company POST
Imports customers into this company (NIF) from an uploaded file. - **`source`:** the origin travels in the body. `csv` is a file following the import template, limited to 5 MB and 1,000 records; `holded` is an Excel (`.xlsx`) exported from Holded contacts, limited to 10 MB and 5,000 records. A file over the limit of its `source` answers `413`. - **This operation writes:** the customers it accepts are created, and a record whose tax identifier already exists in this company is reported as a duplicate rather than created again, so re-importing the same file duplicates nothing. - **`Idempotency-Key`:** required on this operation. - **Rehearsal:** to see what would happen without writing anything, use `POST .../customers/imports/preview`, a separate operation with no effects at all — the import is never governed by a boolean flag.