NewThree filters returned the wrong rows
BeeL
Get StartedMulti-NIFVeriFactuStripeAPI ReferenceChangelog
Customers

Download the customer import template

Scopecustomers:read

Downloads a sample CSV for customer import: the required headers plus example rows, written with a UTF-8 byte order mark so that Excel opens it with the accents intact.

  • Deprecated: use GET /v1/templates/customer-import, which returns the same file. Reading a fixed template is not a POST, and the template belongs to no customer.
  • Retirement: the response announces the retirement date in its Sunset header.

POST
/v1/customers/templates/csv
AuthorizationBearer <token>

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

Response Body

text/csv

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://app.beel.es/api/v1/customers/templates/csv"
"nombre_fiscal;nif;email;telefono;direccion_calle;direccion_numero;direccion_codigo_postal;direccion_poblacion;direccion_provincia\n\"Construcciones García y Asociados SL\";B12345674;contacto@construccionesgarcia.es;915551234;\"Calle Gran Vía\";45;28013;Madrid;Madrid\n"
{
  "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": "FORBIDDEN",
    "message": "You do not have permission to access this resource"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
{
  "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"
  }
}

Delete a customer DELETE

Deletes a customer that has no invoices. - **Deprecated:** use `DELETE /v1/companies/{company_id}/customers/{customer_id}`, which deletes the same way but answers `204` with no body instead of `200`. This route keeps working until the date announced in its `Sunset` response header. ## What deleting means - **No longer exposed:** the customer is retained internally for tax record-keeping purposes, but is no longer exposed by the API: subsequent requests to it return `404`, and it is never included in the customer list, under any value of the `active` filter. - **Identifier released:** its NIF or alternative identifier is freed, so a new customer may be created with the same identifier. ## Customers you cannot delete - **Customers with invoices:** they cannot be deleted and the request answers `409` `CLIENT_HAS_INVOICES`, leaving the customer untouched — neither deleted nor deactivated. - **Deactivating instead:** to stop using a customer, whether or not it has invoices, update it with `active` set to `false`: that releases no identifier and keeps the customer retrievable through `GET /v1/customers?active=false`.

Preview an import of customers from CSV POST

Parses a CSV of customers and returns every row with its validation outcome, the rejected ones included. - **Deprecated:** use `POST /v1/companies/{company_id}/customers/imports/preview`, which parses and validates the file without writing anything — what this route does by default. To actually import, use `POST /v1/companies/{company_id}/customers/imports`, where the origin of the file travels in the body as `source` and a boolean no longer decides between looking and writing. - **File:** must use the headers of the template served by `GET /v1/templates/customer-import`, and is limited to 5 MB and 1,000 rows. - **Validation:** each row is checked against the same rules as single-customer creation, plus duplicate detection within the file and against existing customers, and a NIF lookup in the AEAT register. - **`dry_run`:** at its default `true` nothing is persisted; `false` also persists the valid customers.