---
title: "Creates a customer."
method: POST
path: "/api/v1/customers"
tags: ["Customers"]
---

# Creates a customer.

`POST /api/v1/customers`

## Request

- Base URL: `https://app.flexonthejob.com`
- URL: `https://app.flexonthejob.com/api/v1/customers`
- Auth: HTTP bearer

Requires `ManageCustomers` and an `Idempotency-Key` header (unique per attempt;
a retry with the same key and body replays the first response). The customer is created
active with a zero balance; `country` defaults to `"USA"`; when any of
`address`/`city`/`state`/`postalCode` is given, a "Primary Address"
mailing location is created too (see `/customers/{id}/locations`). The write is
attributed to the user who created the API key.

## Request body

- CustomerWriteRequest: Body of `POST /api/v1/customers` and `PUT /api/v1/customers/{id}`. PUT replaces every editable field, so send the current value of anything you want to keep; omitted optional fields become null. Enum fields accept their names (case-insensitive) or numeric values. Balances (`currentBalance`, `storeCreditBalance`) are maintained by invoicing and cannot be set here.
  - `name` string, required: Display name, e.g. `"Jane Doe"`. Required, ≤ 200 characters.
  - `companyName` string, nullable: Company / trading name, e.g. `"Doe Roofing LLC"`. ≤ 200.
  - `customerType` 'EndCustomer' | 'Installer' | 'Distributor' | 'Commercial'
  - `primaryContact` string, nullable: Primary contact person, if different from `name`. ≤ 100.
  - `email` string, email, nullable: Contact e-mail address. ≤ 256.
  - `phone` string, tel, nullable: Main phone number, stored as entered. ≤ 20.
  - `mobilePhone` string, tel, nullable: Mobile phone number, stored as entered. ≤ 20.
  - `address` string, nullable: Primary (billing) street address line 1. ≤ 200. On create, any of address/city/state/postalCode also creates a "Primary Address" location.
  - `address2` string, nullable: Primary address line 2 (suite, unit). ≤ 200.
  - `city` string, nullable: Primary address city. ≤ 100.
  - `state` string, nullable: Primary address state / province. ≤ 50.
  - `postalCode` string, nullable: Primary address ZIP / postal code. ≤ 20.
  - `country` string, nullable: Primary address country. ≤ 100. Defaults to `"USA"` on create.
  - `taxId` string, nullable: Tax identification number. ≤ 50.
  - `isTaxExempt` boolean, nullable: True when tax exempt; null when not recorded.
  - `priceTier` 'Standard' | 'Silver' | 'Gold' | 'Platinum' | 'Custom'
  - `paymentTerms` 'DueOnReceipt' | 'Net15' | 'Net30' | 'Net45' | 'Net60' | 'COD' | 'Prepaid'
  - `creditLimit` number, double: Credit limit in the organization's currency, e.g. `5000.00`. Default 0.
  - `isActive` boolean: Update only — false deactivates the customer (kept for history, hidden from pickers). Ignored on create; new customers are always active.
  - `status` 'None' | 'Preferred' | 'Difficult'
  - `warningNotes` string, nullable: Warning shown to staff when the customer is selected. ≤ 1000.
  - `notes` string, nullable: Free-form internal notes.

## Response `201`

The new customer; `Location` points at `/api/v1/customers/{id}`.

- CustomerDto: A customer of the calling organization as returned by `GET /api/v1/customers` and `GET /api/v1/customers/{id}`. Money values are decimals in the organization's currency; dates are ISO-8601 UTC. Additional job-site addresses are a separate collection at `/api/v1/customers/{id}/locations`.
  - `id` integer: Numeric id used in URLs (e.g. `/api/v1/customers/42`). Stable within this organization.
  - `syncId` string, uuid: Globally unique sync id (GUID). Use it as the `after` pagination cursor and when correlating with offline devices.
  - `name` string: Display name, e.g. `"Jane Doe"`. Always present.
  - `companyName` string, nullable: Company / trading name when the customer is a business, e.g. `"Doe Roofing LLC"`.
  - `customerType` string: Customer type: `EndCustomer`, `Installer`, `Distributor` or `Commercial`.
  - `primaryContact` string, nullable: Name of the primary contact person, if different from `name`.
  - `email` string, nullable: Contact e-mail address.
  - `phone` string, nullable: Main phone number as entered (no normalisation).
  - `mobilePhone` string, nullable: Mobile phone number as entered.
  - `address` string, nullable: Primary (billing) street address line 1.
  - `address2` string, nullable: Primary address line 2 (suite, unit).
  - `city` string, nullable: Primary address city.
  - `state` string, nullable: Primary address state / province.
  - `postalCode` string, nullable: Primary address ZIP / postal code.
  - `country` string, nullable: Primary address country.
  - `isTaxExempt` boolean, nullable: True when the customer is tax exempt; null when not recorded.
  - `priceTier` string: Price tier applied to this customer: `Standard`, `Silver`, `Gold`, `Platinum` or `Custom`.
  - `paymentTerms` string: Default payment terms: `DueOnReceipt`, `Net15`, `Net30`, `Net45`, `Net60`, `COD` or `Prepaid`.
  - `creditLimit` number, double: Credit limit in the organization's currency, e.g. `5000.00`.
  - `currentBalance` number, double: Outstanding balance owed by the customer, e.g. `1250.50`.
  - `storeCreditBalance` number, double: Store credit available to the customer, e.g. `0.00`.
  - `status` string: Status flag: `None`, `Preferred` or `Difficult`.
  - `taxId` string, nullable: Tax identification number, if recorded.
  - `warningNotes` string, nullable: Warning shown to staff when the customer is selected (e.g. `"Pays late"`).
  - `notes` string, nullable: Free-form internal notes.
  - `isActive` boolean: False when the customer has been deactivated (kept for history, hidden from pickers).
  - `createdAt` string, date-time: UTC creation timestamp, e.g. `2026-03-14T09:12:00Z`.
  - `updatedAt` string, date-time, nullable: UTC timestamp of the last edit; null when never edited after creation.

## Other responses

- `400`: Malformed body, unknown enum value, or missing `Idempotency-Key`.
- `401`: Unauthorized
- `403`: Forbidden
- `409`: The key's creator no longer exists on this server, so the write cannot be attributed.
- `422`: Well-formed but invalid field values (problem lists the fields), or `Idempotency-Key` reused with a different body.
- `429`: Too Many Requests
- `500`: The customer could not be saved.

---

[API](https://skmtc.dev/flexonthejob/apis/flex-api.md) · [All operations](https://skmtc.dev/flexonthejob/apis/flex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flexonthejob/apis/flex-api/revisions/19c8f4d27acf?raw)
