# POST /api/customers

Create customer

Creates a new customer record for payment processing and tokenization.

**Operation ID:** `customersCreate`

## Authorization

Requires: Customers.Customers, Customers.Customers.Create, merchant scope.

Required permissions:
- `Customers.Customers`
- `Customers.Customers.Create`

## Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| suppressNulls | query | no | boolean | If true, omit properties with null values. |

## Request Body

**Content type:** `application/json`

Schema: `CustomerCreateDto`

Properties:
- `createdFromTemplateId` (string(uuid))
- `name` (string) required: Gets or sets the name of the customer. Must be unique within the owning merchant, compared  case-insensitively. Use `displayName` for a human label that may repeat.
- `displayName` (string): An optional human-readable label for the customer that is deliberately not unique. Supply it  when the recognisable name for two customers is legitimately the same and `name`  carries an external key instead. When present it is what merchants see wherever customers are  listed; when absent those surfaces fall back to `name`. At most 100 characters.
- `merchantAssignedId` (string): An optional identifier the merchant assigns to the customer as a friendly reference, distinct  from the system Guid `id` and the server-owned legacy number. Surfaces the v1  API's `CustomerId` field. Not an `EditFormField`: it is set through the API rather  than the standard customer form.
- `merchantId` (string(uuid)) required: Gets or sets the identifier of the merchant that owns this customer.
- `isActive` (boolean): Gets or sets a value indicating whether this customer is active.
- `contactDetail` (object): Gets or sets the customer's contact details (email, phone, address).
- `tags` (array<EntityTag>): Gets or sets the collection of tags used to categorize or label this customer.
- `storedPaymentMethods` (array<CustomerStoredPaymentMethod>): Gets or sets the customer's stored payment methods for recurring or future transactions.
- `contracts` (array<Contract>): Gets or sets the recurring billing contracts associated with this customer.
- `notes` (array<EntityNote>): Operational notes attached to the customer.

_Example: Basic Customer_

Minimal customer record with name, contact info, and a default mailing address. To attach a stored card or ACH account, follow up with POST /api/customer/customers/{id}/stored-payment-methods (see AddStoredPaymentMethodInput examples). That endpoint tokenizes the payment data server-side. Do not embed raw card or ACH data in StoredPaymentMethods on this payload; the customer CRUD path does not tokenize embedded payment methods.

```json
{
  "name": "John Doe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "John Doe",
    "emailAddress": "john.doe@example.com",
    "phone": "555-555-0100"
  }
}
```

_Example: Business Customer_

Business customer with company URL and corporate contact. Same two-step flow applies for stored payment methods: create the customer first, then call the dedicated stored-payment-methods endpoint to tokenize each card or ACH account.

```json
{
  "name": "Acme Corp",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "1 Corporate Way",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "Accounts Payable",
    "emailAddress": "ap@acme.example.com",
    "phone": "555-555-0200",
    "companyUrl": "https://acme.example.com"
  }
}
```

_Example: Customer With No Address On File_

The sanctioned representation for a customer whose address you do not hold yet: send an empty addresses array, or omit the member entirely. Both are accepted. Do not send a placeholder address to satisfy the endpoint; a made-up address is indistinguishable from a real one once it is on the record. Add the real address later with PUT /api/customers/{id} once the payer has supplied it. An address you do send must be complete (address1, city, state, zip) and exactly one entry must set isDefault.

```json
{
  "name": "Jane Roe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "primaryContactName": "Jane Roe",
    "emailAddress": "jane.roe@example.com",
    "phone": "555-555-0300"
  }
}
```

**Content type:** `text/json`

Schema: `CustomerCreateDto`

Properties:
- `createdFromTemplateId` (string(uuid))
- `name` (string) required: Gets or sets the name of the customer. Must be unique within the owning merchant, compared  case-insensitively. Use `displayName` for a human label that may repeat.
- `displayName` (string): An optional human-readable label for the customer that is deliberately not unique. Supply it  when the recognisable name for two customers is legitimately the same and `name`  carries an external key instead. When present it is what merchants see wherever customers are  listed; when absent those surfaces fall back to `name`. At most 100 characters.
- `merchantAssignedId` (string): An optional identifier the merchant assigns to the customer as a friendly reference, distinct  from the system Guid `id` and the server-owned legacy number. Surfaces the v1  API's `CustomerId` field. Not an `EditFormField`: it is set through the API rather  than the standard customer form.
- `merchantId` (string(uuid)) required: Gets or sets the identifier of the merchant that owns this customer.
- `isActive` (boolean): Gets or sets a value indicating whether this customer is active.
- `contactDetail` (object): Gets or sets the customer's contact details (email, phone, address).
- `tags` (array<EntityTag>): Gets or sets the collection of tags used to categorize or label this customer.
- `storedPaymentMethods` (array<CustomerStoredPaymentMethod>): Gets or sets the customer's stored payment methods for recurring or future transactions.
- `contracts` (array<Contract>): Gets or sets the recurring billing contracts associated with this customer.
- `notes` (array<EntityNote>): Operational notes attached to the customer.

_Example: Basic Customer_

Minimal customer record with name, contact info, and a default mailing address. To attach a stored card or ACH account, follow up with POST /api/customer/customers/{id}/stored-payment-methods (see AddStoredPaymentMethodInput examples). That endpoint tokenizes the payment data server-side. Do not embed raw card or ACH data in StoredPaymentMethods on this payload; the customer CRUD path does not tokenize embedded payment methods.

```json
{
  "name": "John Doe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "John Doe",
    "emailAddress": "john.doe@example.com",
    "phone": "555-555-0100"
  }
}
```

_Example: Business Customer_

Business customer with company URL and corporate contact. Same two-step flow applies for stored payment methods: create the customer first, then call the dedicated stored-payment-methods endpoint to tokenize each card or ACH account.

```json
{
  "name": "Acme Corp",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "1 Corporate Way",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "Accounts Payable",
    "emailAddress": "ap@acme.example.com",
    "phone": "555-555-0200",
    "companyUrl": "https://acme.example.com"
  }
}
```

_Example: Customer With No Address On File_

The sanctioned representation for a customer whose address you do not hold yet: send an empty addresses array, or omit the member entirely. Both are accepted. Do not send a placeholder address to satisfy the endpoint; a made-up address is indistinguishable from a real one once it is on the record. Add the real address later with PUT /api/customers/{id} once the payer has supplied it. An address you do send must be complete (address1, city, state, zip) and exactly one entry must set isDefault.

```json
{
  "name": "Jane Roe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "primaryContactName": "Jane Roe",
    "emailAddress": "jane.roe@example.com",
    "phone": "555-555-0300"
  }
}
```

**Content type:** `application/*+json`

Schema: `CustomerCreateDto`

Properties:
- `createdFromTemplateId` (string(uuid))
- `name` (string) required: Gets or sets the name of the customer. Must be unique within the owning merchant, compared  case-insensitively. Use `displayName` for a human label that may repeat.
- `displayName` (string): An optional human-readable label for the customer that is deliberately not unique. Supply it  when the recognisable name for two customers is legitimately the same and `name`  carries an external key instead. When present it is what merchants see wherever customers are  listed; when absent those surfaces fall back to `name`. At most 100 characters.
- `merchantAssignedId` (string): An optional identifier the merchant assigns to the customer as a friendly reference, distinct  from the system Guid `id` and the server-owned legacy number. Surfaces the v1  API's `CustomerId` field. Not an `EditFormField`: it is set through the API rather  than the standard customer form.
- `merchantId` (string(uuid)) required: Gets or sets the identifier of the merchant that owns this customer.
- `isActive` (boolean): Gets or sets a value indicating whether this customer is active.
- `contactDetail` (object): Gets or sets the customer's contact details (email, phone, address).
- `tags` (array<EntityTag>): Gets or sets the collection of tags used to categorize or label this customer.
- `storedPaymentMethods` (array<CustomerStoredPaymentMethod>): Gets or sets the customer's stored payment methods for recurring or future transactions.
- `contracts` (array<Contract>): Gets or sets the recurring billing contracts associated with this customer.
- `notes` (array<EntityNote>): Operational notes attached to the customer.

_Example: Basic Customer_

Minimal customer record with name, contact info, and a default mailing address. To attach a stored card or ACH account, follow up with POST /api/customer/customers/{id}/stored-payment-methods (see AddStoredPaymentMethodInput examples). That endpoint tokenizes the payment data server-side. Do not embed raw card or ACH data in StoredPaymentMethods on this payload; the customer CRUD path does not tokenize embedded payment methods.

```json
{
  "name": "John Doe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "John Doe",
    "emailAddress": "john.doe@example.com",
    "phone": "555-555-0100"
  }
}
```

_Example: Business Customer_

Business customer with company URL and corporate contact. Same two-step flow applies for stored payment methods: create the customer first, then call the dedicated stored-payment-methods endpoint to tokenize each card or ACH account.

```json
{
  "name": "Acme Corp",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "1 Corporate Way",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "Accounts Payable",
    "emailAddress": "ap@acme.example.com",
    "phone": "555-555-0200",
    "companyUrl": "https://acme.example.com"
  }
}
```

_Example: Customer With No Address On File_

The sanctioned representation for a customer whose address you do not hold yet: send an empty addresses array, or omit the member entirely. Both are accepted. Do not send a placeholder address to satisfy the endpoint; a made-up address is indistinguishable from a real one once it is on the record. Add the real address later with PUT /api/customers/{id} once the payer has supplied it. An address you do send must be complete (address1, city, state, zip) and exactly one entry must set isDefault.

```json
{
  "name": "Jane Roe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "primaryContactName": "Jane Roe",
    "emailAddress": "jane.roe@example.com",
    "phone": "555-555-0300"
  }
}
```

## Responses

### 200

OK

**Content type:** `application/json`

Schema: `CustomerDto`

Properties:
- `id` (string(uuid))
- `creationTime` (string(date-time)): The date and time when this entity was created.
- `creatorId` (string(uuid)): The ID of the user who created this entity.
- `lastModificationTime` (string(date-time)): The date and time when this entity was last modified.
- `lastModifierId` (string(uuid)): The ID of the user who last modified this entity.
- `isDeleted` (boolean): Indicates whether this entity has been deleted.
- `deleterId` (string(uuid)): The ID of the user who deleted this entity, if it is deleted.
- `deletionTime` (string(date-time)): The date and time when this entity was deleted, if it is deleted.
- `createdFromTemplateId` (string(uuid)): The template this record was created from, or `null` for one started blank. Set by the  create-from-template path and by the add/edit page's Load Template action when the record is  saved.
- `name` (string): Gets or sets the name of the customer. Unique within the owning merchant, compared  case-insensitively.
- `displayName` (string): An optional human-readable label for the customer that is deliberately not unique. Null when  the customer carries no separate label, in which case `name` is what merchants  see wherever customers are listed.
- `concurrencyStamp` (string): Gets or sets the concurrency stamp used for optimistic concurrency control.
- `tenantId` (string(uuid)): Gets the tenant identifier for multi-tenancy isolation.
- `merchantId` (string(uuid)): Gets or sets the identifier of the merchant that owns this customer.
- `isActive` (boolean): Gets or sets a value indicating whether this customer is active.
- `legacyNumber` (integer(int64)): Gets or sets the customer's legacy numeric key, the integer identifier the v1 API resolves  customers by. Server-owned: stamped at create and ignored on every inbound payload. Null on  customers created before the field existed and not yet back-filled by the v1 data migration.
- `merchantAssignedId` (string): An optional identifier the merchant assigns to the customer as a friendly reference, distinct  from the system Guid `Id` and the server-owned `legacyNumber`. Surfaces  the v1 API's `CustomerId` field.
- `contactDetail` (object): Gets or sets the customer's contact details (email, phone, address).
- `tags` (array<EntityTag>): Gets or sets the collection of tags used to categorize or label this customer.
- `storedPaymentMethods` (array<CustomerStoredPaymentMethod>): Gets or sets the customer's stored payment methods for recurring or future transactions.
- `contracts` (array<Contract>): Gets or sets the recurring billing contracts associated with this customer.
- `entityVersion` (integer(int32)): Gets the entity version, incremented on each modification for optimistic concurrency.
- `extraProperties` (object): Gets the extra properties dictionary for extensible data storage.
- `notes` (array<EntityNote>): Gets or sets operational notes attached to the customer.

### 403

Forbidden

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 401

Unauthorized

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 400

Bad Request

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 404

Not Found

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 501

Not Implemented

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 500

Internal Server Error

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### default

The request failed. The body carries the standard error envelope: a machine-readable `error.code`, a human-readable `error.message`, and `error.validationErrors` when the failure was a validation rejection. See the error-code reference in this document's description for the values `error.code` can take.

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

### 429

The request was refused because a rate limit was exceeded, or because something a later retry can clear stopped it. A rate limit refusal carries an `application/problem+json` body: wait at least the interval `Retry-After` names before retrying, then back off. Limits are tuned per deployment, so read the allowance from the response headers rather than assuming a fixed ceiling. Any other refusal carries the standard error envelope as `application/json`, and its `error.code` names the cause.

**Content type:** `application/problem+json`

Schema: `RateLimitProblemDetails`

Properties:
- `type` (string) required: The problem type identifier. Always the same value: the failure is the status code itself,  so there is no sub-type for a caller to branch on.
- `title` (string) required: A short, human-readable summary of the problem type.
- `status` (integer(int32)) required: The HTTP status code, repeated in the body as the problem-details format defines.
- `detail` (string) required: A human-readable explanation of this occurrence of the problem.
- `retryAfterSeconds` (integer(int32)) required: How long to wait before retrying, in whole seconds, carrying the same figure as the  `Retry-After` header. Always at least one: a value of zero would invite an immediate  retry that is certain to be rejected again.

**Content type:** `application/json`

Schema: `RemoteServiceErrorResponse`

Properties:
- `error` (RemoteServiceErrorInfo)

## Example request

Every block below sends the same request. Replace {{BASE_URL}} with the address of the API you are calling and {{API_KEY}} with your own key.

The request body is a CustomerCreateDto. See the Request body section below for its fields.

### cURL

```bash
curl -X POST "{{BASE_URL}}/api/customers" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "John Doe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "John Doe",
    "emailAddress": "john.doe@example.com",
    "phone": "555-555-0100"
  }
}'
```

### PowerShell

```powershell
$headers = @{
    'api-key' = '{{API_KEY}}'
}

$body = @'
{
  "name": "John Doe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "John Doe",
    "emailAddress": "john.doe@example.com",
    "phone": "555-555-0100"
  }
}
'@

$response = Invoke-RestMethod -Method POST -Uri '{{BASE_URL}}/api/customers' `
    -Headers $headers -ContentType 'application/json' -Body $body
```

### TypeScript (SDK)

```bash
npm install @winkpg/winkpg-api
```

```typescript
import { Configuration, CustomersApi } from '@winkpg/winkpg-api';

const api = new CustomersApi(new Configuration({
  basePath: '{{BASE_URL}}',
  apiKey: '{{API_KEY}}',
}));

const { data } = await api.customersCreate({
  "name": "John Doe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": true,
  "contactDetail": {
    "addresses": [
      {
        "address1": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": true,
        "isDefault": true
      }
    ],
    "primaryContactName": "John Doe",
    "emailAddress": "john.doe@example.com",
    "phone": "555-555-0100"
  }
});
```

### TypeScript (raw HTTP)

```typescript
const response = await fetch('{{BASE_URL}}/api/customers', {
  method: 'POST',
  headers: {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "John Doe",
    "merchantId": "00000000-0000-0000-0000-000000000001",
    "isActive": true,
    "contactDetail": {
      "addresses": [
        {
          "address1": "123 Main St",
          "city": "Phoenix",
          "state": "AZ",
          "zip": "85027",
          "country": "USA",
          "isMailing": true,
          "isDefault": true
        }
      ],
      "primaryContactName": "John Doe",
      "emailAddress": "john.doe@example.com",
      "phone": "555-555-0100"
    }
  }),
});

const data = await response.json();
```

### C# (SDK)

```bash
dotnet add package WinkPg.Api.Client
```

```csharp
using WinkPg.Api.Client.Api;
using WinkPg.Api.Client.Client;
using System.Text.Json;

var config = new Configuration { BasePath = "{{BASE_URL}}" };
config.AddApiKey("api-key", "{{API_KEY}}");

var api = new CustomersApi(config);
var body = JsonSerializer.Deserialize<CustomerCreateDto>("""
    {
      "name": "John Doe",
      "merchantId": "00000000-0000-0000-0000-000000000001",
      "isActive": true,
      "contactDetail": {
        "addresses": [
          {
            "address1": "123 Main St",
            "city": "Phoenix",
            "state": "AZ",
            "zip": "85027",
            "country": "USA",
            "isMailing": true,
            "isDefault": true
          }
        ],
        "primaryContactName": "John Doe",
        "emailAddress": "john.doe@example.com",
        "phone": "555-555-0100"
      }
    }
    """);

var result = await api.CustomersCreateAsync(body);
```

### C# (raw HTTP)

```csharp
using System.Text;

using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };

var request = new HttpRequestMessage(new HttpMethod("POST"), "/api/customers");
request.Headers.Add("api-key", "{{API_KEY}}");

request.Content = new StringContent("""
    {
      "name": "John Doe",
      "merchantId": "00000000-0000-0000-0000-000000000001",
      "isActive": true,
      "contactDetail": {
        "addresses": [
          {
            "address1": "123 Main St",
            "city": "Phoenix",
            "state": "AZ",
            "zip": "85027",
            "country": "USA",
            "isMailing": true,
            "isDefault": true
          }
        ],
        "primaryContactName": "John Doe",
        "emailAddress": "john.doe@example.com",
        "phone": "555-555-0100"
      }
    }
    """, Encoding.UTF8, "application/json");

var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
```

### Python (SDK)

```bash
pip install winkpg-api
```

```python
import winkpg_api

configuration = winkpg_api.Configuration(host="{{BASE_URL}}")
configuration.api_key["ApiKey"] = "{{API_KEY}}"

with winkpg_api.ApiClient(configuration) as client:
    api = winkpg_api.CustomersApi(client)
    body = winkpg_api.CustomerCreateDto.from_dict({
      "name": "John Doe",
      "merchantId": "00000000-0000-0000-0000-000000000001",
      "isActive": True,
      "contactDetail": {
        "addresses": [
          {
            "address1": "123 Main St",
            "city": "Phoenix",
            "state": "AZ",
            "zip": "85027",
            "country": "USA",
            "isMailing": True,
            "isDefault": True
          }
        ],
        "primaryContactName": "John Doe",
        "emailAddress": "john.doe@example.com",
        "phone": "555-555-0100"
      }
    })
    result = api.customers_create(body)
```

### Python (raw HTTP)

```bash
pip install requests
```

```python
import requests

headers = {
    "api-key": "{{API_KEY}}",
    "Content-Type": "application/json",
}

body = {
  "name": "John Doe",
  "merchantId": "00000000-0000-0000-0000-000000000001",
  "isActive": True,
  "contactDetail": {
    "addresses": [
      {
        "address1": "123 Main St",
        "city": "Phoenix",
        "state": "AZ",
        "zip": "85027",
        "country": "USA",
        "isMailing": True,
        "isDefault": True
      }
    ],
    "primaryContactName": "John Doe",
    "emailAddress": "john.doe@example.com",
    "phone": "555-555-0100"
  }
}

response = requests.request(
    "POST",
    "{{BASE_URL}}/api/customers",
    headers=headers,
    json=body,
)
response.raise_for_status()
data = response.json()
```

## See also

- [All documentation](https://devportal-simpay-sbx.winkpg.io/llms.txt): the machine-readable index of every public page on this site.
