# Create a customer

```http
POST https://secure.yoprint.com/v1/api/store/{team_slug}/customer
```

- API version: v1
- Operation ID: `v1_public_customer_store`
- Section: Customers › [Customers](https://developer.yoprint.com/reference/customers.md)
- Web page: https://developer.yoprint.com/reference/customers/v1-customer-store/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Creates a customer, with its contacts and its billing and shipping addresses, in one request. Returns the new customer with `contacts` and `addresses`.

Only `customer.name` and `customer.tax_exempt` are required. A sales order names one of the customer's contacts and its billing and shipping addresses (`customer_contact_id`, `billing_address_id`, `shipping_address_id`), so send them here or add them later. A contact needs at least a first name.

- Mark one contact `is_primary: true` to make it the primary contact. If none is marked, the customer has no primary contact until you set one.
- The first billing address and the first shipping address become primary, whatever `is_primary` says.
- You are assigned to the new customer.

When you later create a sales order or quote for this customer, it takes the customer's primary brand profile, default payment term, default pricing group and default warehouse. A default pricing group that was removed since is skipped: the order gets the team's default group.

## Permission

Needs the Manage Customers permission (either level can create customers).

## Business rules

- `is_external` and `external_type` are ignored on customers and their contacts: sending them (for example a value you read back) is not refused and changes nothing. Only the Shopify sync sets them. Other fields not listed here are not part of the API.
- Everything is saved together or not at all.
- Each contact needs `first_name`, `send_invoice` and `is_primary`. A phone needs `phone_country` and must be a valid number for that country.
- Each address needs `label`, `address1`, `country_code` and `is_primary`. For `US` and `CA`, `state` and `state_code` must name a real state or province; YoPrint fills in the other half of the pair.
- At most one contact, one billing address and one shipping address may be sent with `is_primary: true` (422 "You cannot set more than 1 primary address").
- Nothing checks for duplicate customers: two customers may share a name, an email or a phone. To find or create by contact email or phone, use Create or update a customer by contact (`customer/upsert`).
- On a live customer (not a system or Shopify customer) a contact's email is saved trimmed and in lower case, and two live contacts of the customer can't have the same email (compared the same way). Other customers may use the same email. So two contacts in one request can't share an email.
- Only `customer.default_payment_term_id` is read as an id on create. To set a default pricing group or default warehouse, create the customer, then update it.

## Side effects

- Sends the `customer.created` webhook.
- Assigns you to the customer.
- When the contact's email already belongs to a Customer Portal account on your team that can't open this customer yet, saving the contact gives that account access to this customer (active when the account is set up with a verified email or a password, invited otherwise). No email is sent.

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.

## Request body

Content type `application/json`, required.

Fields:

- `customer` (object, required)
  - `name` (string, required): The customer's name, usually the company. Names need not be unique.
  - `tax_exempt` (boolean, required): True when no sales tax applies to this customer.
  - `customer_portal_enabled` (boolean | null, optional): `false` creates the customer with the Customer Portal off: its contacts can't open the portal. Leave it out, or send `true` or null, for on.
  - `customer_portal_payment_option` (string | null, optional): Whether the customer can pay online in the customer portal. Leave it out to follow the team setting. One of: `"ENABLED"`, `"DISABLED"`.
  - `default_payment_term_id` (string | null, optional): A payment term id. New sales orders for this customer use it.
  - `hide` (boolean, optional): True creates the customer archived. Not checked on create: send a JSON boolean.
  - `internal_notes` (string, optional): Notes for your team only.
  - `resale_number` (string, optional): The customer's resale or tax certificate number.
  - `third_party_billing_default` (string, optional): How the customer's shipping labels are billed by default (see the customer object). Leave it out for `none`. One of: `"none"`, `"default_carrier_account"`.
  - `vat_number` (string, optional): The customer's tax number (Tax number in YoPrint).
  - `website` (string, optional): The website address.
- `billing` (array of objects, optional): Billing addresses. At most one may have `is_primary: true`.
  - Each item:
    - `address1` (string, required): Required. Street line 1.
    - `country_code` (string, required): Required. Two-letter country code, such as `US`.
    - `is_primary` (boolean, required): Required, but the first address of each role always becomes primary and later ones never do. Use the set-primary operation to change it.
    - `label` (string, required): Required. A name for the address, shown in pickers: "Head office".
    - `address2` (string, optional): Street line 2, such as a suite or unit.
    - `city` (string, optional): The city.
    - `country` (string, optional): The country name. Shown as the country of `country_code` when blank.
    - `note` (string, optional): A note kept with the address, such as delivery instructions.
    - `postcode` (string, optional): The postal or ZIP code.
    - `state` (string, optional): The state or province name, such as "Illinois". For US and Canada addresses, send either `state` or `state_code` (or both matching): YoPrint resolves and stores both. For other countries, send the name in `state`. Sending `null` is ignored; omit the field to leave it unset.
    - `state_code` (string, optional): The two-letter state or province code, such as `IL` or `ON`. Used for US and Canada addresses: send either `state_code` or `state`. Sending `null` is ignored; omit the field to leave it unset.
- `contact` (array of objects, optional): The customer's contacts. At most one may have `is_primary: true`.
  - Each item:
    - `first_name` (string, required): Required. The contact's first name.
    - `is_primary` (boolean, required): Required.
    - `send_invoice` (boolean, required): Required. Stored with the contact and returned as `send_invoice`.
    - `default_receive_sms` (boolean, optional): When this contact is set on a sales order, it receives text messages about the order by default. Saved as sent; defaults to false.
    - `email` (string (email), optional): The contact's email. Messages you send to this contact go here.
    - `fax` (string, optional): Checked as a real number for `fax_country`.
    - `fax_country` (string, optional): Two-letter country code of `fax`.
    - `last_name` (string, optional): The contact's last name.
    - `phone` (string, optional): Checked as a real number for `phone_country`. Send `phone_country` with it.
    - `phone_country` (string, optional): Two-letter country code of `phone`, such as `US`. Required with `phone`.
- `shipping` (array of objects, optional): Shipping addresses. At most one may have `is_primary: true`. Same fields as `billing` above.

### Example request

```bash
curl -X POST 'https://secure.yoprint.com/v1/api/store/your-team/customer' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "billing": [
      {
        "address1": "100 Example Street",
        "address2": "Suite 200",
        "city": "Springfield",
        "country_code": "US",
        "is_primary": true,
        "label": "Head office",
        "postcode": "62701",
        "state_code": "IL"
      }
    ],
    "contact": [
      {
        "email": "alex@example.com",
        "first_name": "Alex",
        "is_primary": true,
        "last_name": "Rivera",
        "phone": "+1 201 555 0123",
        "phone_country": "US",
        "send_invoice": true
      }
    ],
    "customer": {
      "name": "Northwind Apparel",
      "tax_exempt": false,
      "website": "https://example.com"
    },
    "shipping": [
      {
        "address1": "100 Example Street",
        "address2": "Dock 4",
        "city": "Springfield",
        "country_code": "US",
        "is_primary": true,
        "label": "Warehouse",
        "postcode": "62701",
        "state_code": "IL"
      }
    ]
  }'
```

Another example: Just the required fields:

```bash
curl -X POST 'https://secure.yoprint.com/v1/api/store/your-team/customer' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "customer": {
      "name": "Northwind Apparel",
      "tax_exempt": false
    }
  }'
```

## Response

`200` Successful response.

Fields:

- `data` (object, required)
  - `created_at` (string (date-time), required)
  - `hide` (boolean, required): True when the customer is archived.
  - `id` (string, required): The customer id. Use it in paths and as `customer_id` on sales orders and quotes.
  - `is_editable` (boolean, required): False for Shopify and system customers: they can't be changed through this API.
  - `is_external` (boolean, required)
  - `is_system` (boolean, required)
  - `name` (string | null, required): Null for a customer created by upsert without a name.
  - `tax_exempt` (boolean, required)
  - `trashed` (boolean, required): True when the customer is deleted.
  - `updated_at` (string (date-time), required)
  - `account_warning` (object | null, optional): Why YoPrint shows a warning next to the customer's name. Null when there is nothing to warn about.
    - `open_dispute` (object | null, required): The customer's payments under a dispute that is open or under review; null when there is none.
      - `count` (integer, required): How many payments are disputed.
      - `payment_amount` (integer | number | string, required): The total of the disputed payments, each counted once. Thousandths, an older encoding: `125000` is 125.00. See [Money and quantities](/concepts/money-and-quantities/).
    - `owed_amount` (integer | number | string | null, required): What the customer owes back because `wallet_fund` is negative, as a positive amount; null when it is not negative. Thousandths, an older encoding: `125000` is 125.00. See [Money and quantities](/concepts/money-and-quantities/).
  - `addresses` (array of objects, optional): Billing and shipping addresses that are not deleted, old copies replaced by an edit included (`is_current_source: false`).
    - Each item:
      - `address1` (string | null, required)
      - `id` (string, required)
      - `is_current_source` (boolean, required): False for deleted, replaced, Shopify or redacted addresses. Only current addresses can be picked for new documents or edited.
      - `is_primary` (boolean, required)
      - `label` (string | null, required)
      - `revision_number` (integer, required): Goes up by one on every saved change. Send it back as `base_revision_number` when updating the address to protect against concurrent edits.
      - `role` (string, required): `billing` or `shipping` for customer addresses.
      - `trashed` (boolean, required)
      - `address2` (string | null, optional)
      - `address_family_id` (string | null, optional): Shared by an address and the copies made when it was edited after use.
      - `city` (string | null, optional)
      - `copied_from_address_id` (string | null, optional)
      - `country` (string | null, optional)
      - `country_code` (string | null, optional)
      - `customer_id` (string, optional): Customer addresses only: the customer's id.
      - `external_type` (string | null, optional): `shopify` for an address synced from Shopify.
      - `hold_stock` (boolean, optional): Company locations only; false for customer addresses.
      - `is_default_warehouse` (boolean, optional): Company locations only; false for customer addresses.
      - `is_external` (boolean, optional): True for an address synced from Shopify; such addresses are read-only.
      - `nexus` (boolean, optional): Company locations only; false for customer addresses.
      - `note` (string | null, optional)
      - `owner_id` (string | null, optional): The owner's id: for a customer address, the customer's id (the same as `customer_id`).
      - `owner_type` (string, optional): `customer` for customer addresses. One of: `"customer"`, `"team"`, `"unknown"`.
      - `postal_can_validate` (boolean, optional)
      - `postal_is_valid` (boolean, optional)
      - `postal_validated` (boolean, optional): True once YoPrint has checked the address with its address validator. `postal_can_validate` says whether it could be checked and `postal_is_valid` gives the result. Any edit resets it to false.
      - `postcode` (string | null, optional)
      - `source` (string, optional): `customer` for customer addresses. One of: `"customer"`, `"company_location"`, `"custom"`, `"unknown"`.
      - `state` (string | null, optional)
      - `state_code` (string | null, optional)
      - `superseded_at` (string (date-time) | null, optional)
      - `superseded_by_address_id` (string | null, optional): Set on an old copy: the address that replaced it.
      - `used_by_active_profile` (boolean, optional): True when a brand profile uses it as Ship From. Sent on lists, reads, updates and customer answers; not on create or set-primary.
  - `assigned_users` (array of objects, optional): The users assigned to the customer. Unlike List the users assigned to a customer, people who have left the team stay in this list.
    - Each item:
      - `avatar_url` (string | null, required)
      - `id` (string, required)
      - `name` (string | null, required)
  - `contacts` (array of objects, optional): Every contact, deleted ones included (`trashed`).
    - Each item:
      - `created_at` (string (date-time), required)
      - `first_name` (string | null, required)
      - `id` (string, required)
      - `is_primary` (boolean, required)
      - `trashed` (boolean, required): True when the contact is deleted.
      - `updated_at` (string (date-time), required)
      - `customer_id` (string, optional): Customer contacts only.
      - `default_receive_sms` (boolean, optional)
      - `email` (string | null, optional)
      - `external_type` (string | null, optional)
      - `fax` (string | null, optional)
      - `fax_country` (string | null, optional)
      - `is_external` (boolean, optional): True for a contact synced from Shopify; such contacts are read-only.
      - `last_name` (string | null, optional)
      - `phone` (string | null, optional)
      - `phone_country` (string | null, optional)
      - `send_invoice` (boolean, optional): Customer contacts only.
      - `vendor_id` (string, optional): Vendor contacts only.
  - `customer_portal_enabled` (boolean | null, optional): Whether the customer's contacts can open the Customer Portal (Portal settings > Customer portal in YoPrint). `false`: the portal is off for this customer. `true`, or null (never set), means on.
  - `customer_portal_id` (string | null, optional): The id used in the customer's portal address. Null for Shopify customers.
  - `customer_portal_id_override` (string | null, optional)
  - `customer_portal_link` (null, optional): Always null: YoPrint no longer returns a portal sign-in link for the customer. The field stays for older integrations.
  - `customer_portal_payment_option` (string | null, optional): Whether the customer can pay online in the portal. Null follows the team setting (and is always null for Shopify customers). One of: `"ENABLED"`, `"DISABLED"`.
  - `customer_profiles` (array of objects, optional): Brand profiles.
    - Each item:
      - `address_id` (string | null, required): The Ship From address, or null for a profile that has none.
      - `company_name` (string | null, required)
      - `customer_id` (string, required)
      - `id` (string, required)
      - `is_primary` (boolean, required): New sales orders and quotes for the customer use the primary profile.
      - `trashed` (boolean, required)
      - `use_custom_logo` (boolean, required)
      - `address` (object | null, optional): The Ship From address (an address, as in Customer addresses, without `used_by_active_profile`), or null. Same fields as `data.addresses` above.
      - `email` (string | null, optional)
      - `logo` (object | null, optional): The newest logo file (a file, as in Customer files), or null.
        - `collection` (string, required): Customer files: `tax_exemption`, `internal_file` or `external_file`. Vendor files: `default`, `mockup` or `production_file`.
        - `created_at` (string (date-time), required)
        - `file_name` (string, required)
        - `id` (string, required)
        - `mime_type` (string | null, required)
        - `name` (string | null, required): The display name.
        - `size` (integer, required): Bytes.
        - `custom_properties` (object | array, optional)
        - `customer_id` (string, optional): Customer files only.
        - `dimensions` (object | null, optional): Pixel size of an image, read from its header; null for other files and for images uploaded before the size was recorded.
          - `height` (integer, optional)
          - `width` (integer, optional)
        - `is_image` (boolean, optional)
        - `position` (integer | null, optional): Where the file sorts.
        - `preview` (object | null, optional): Preview image links `small`, `medium` and `large` (250, 800 and 1200 px), or null when there is no preview.
        - `preview_state` (string, optional): `ready`: the preview links work. `processing`: an image over 10 MB whose stored preview a worker is still making (seconds); read the file again, or in the web app wait for `media.preview_state_changed` on the team channel. `too_large`: an image over 120 megapixels; it never gets a preview. `failed`: the worker gave up. `none`: not an image. One of: `"ready"`, `"processing"`, `"too_large"`, `"failed"`, `"none"`.
        - `show_in_pdf` (boolean | null, optional)
        - `source_media_id` (string | null, optional)
        - `url` (string | null, optional): Where to download the file.
        - `view_url` (string | null, optional): A signed link to view the file. It expires: read it fresh, and don't store it.
      - `phone` (string | null, optional)
      - `website` (string | null, optional)
  - `default_payment_term_id` (string | null, optional)
  - `default_pricing_group_id` (string | null, optional)
  - `default_warehouse_id` (string | null, optional)
  - `deposit_due_amount` (integer | number | string, optional): Deposits still due. Thousandths, an older encoding: `125000` is 125.00. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `display_customer_portal_id` (string, optional): The Customer Portal ID shown to people: the override when set, otherwise `customer_portal_id`. `-` for Shopify customers.
  - `external_type` (string | null, optional): `shopify` for customers synced from Shopify.
  - `has_customer_portal_id_override` (boolean, optional)
  - `held_credit` (integer | number | string, optional): The part of `wallet_fund` held while a payment is under an open dispute or a provider review: it can't be applied to orders or taken out until the hold ends. Never more than `wallet_fund`; 0 when `wallet_fund` is 0 or negative. Thousandths, an older encoding: `125000` is 125.00. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `internal_notes` (string | null, optional)
  - `last_ordered_date` (string (date-time) | null, optional): The issue date of the customer's latest published sales order. Null when it has none.
  - `media` (array of objects, optional): Same fields as `data.customer_profiles[].logo` above.
  - `ordered_amount` (integer | number | string, optional): Total of the customer's published sales orders. Thousandths, an older encoding: `125000` is 125.00. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `overdue_amount` (integer | number | string, optional): Unpaid amounts past their payment due date. Thousandths, an older encoding: `125000` is 125.00. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `pdf` (object, optional)
    - `invoice_statement` (string, required): A link that downloads the customer's invoice statement PDF. Its token opens only this customer's statement and stops working 9,000 hours (about a year) after the answer was made. Anyone with the link can download the statement: don't share it.
  - `quote_amount` (integer | number | string, optional): Total of open quotes. Thousandths, an older encoding: `125000` is 125.00. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `resale_number` (string | null, optional)
  - `shopify_compliance_deletion` (object | null, optional)
  - `system_type` (string | null, optional)
  - `tags` (array of objects, optional)
    - Each item:
      - `color_hex` (string | null, required)
      - `id` (string, required)
      - `name` (string, required)
  - `third_party_billing_default` (string, optional): How a shipping label for this customer is billed by default. `default_carrier_account`: YoPrint's label and rate dialogs start on the customer's default carrier account (third-party billing). `none`: they start with no third-party billing. One of: `"none"`, `"default_carrier_account"`.
  - `unpaid_amount` (integer | number | string, optional): What the customer still owes on published sales orders. Thousandths, an older encoding: `125000` is 125.00. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `vat_number` (string | null, optional): The customer's tax number (shown as Tax number in YoPrint).
  - `wallet_currency` (object, optional): The team's default currency, the one `wallet_fund` is in. `iso` is its code.
    - `default` (boolean, required)
    - `id` (string, required)
    - `iso` (string | null, required): The ISO 4217 code, such as `USD`.
    - `name` (string | null, required)
    - `symbol` (string | null, required)
    - `trashed` (boolean, required)
  - `wallet_fund` (integer | number | string, optional): Customer credit balance. Negative when the customer owes credit back, for example after a refund of a payment whose credit was already spent. Thousandths, an older encoding: `125000` is 125.00. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (`1234.5` is 1.2345), or as a string (`"1234.5"`) when you send `X-Yoprint-Decimal-Format: exact-v1`. See [Money and quantities](/concepts/money-and-quantities/).
  - `website` (string | null, optional)
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": {
    "account_warning": null,
    "addresses": [
      {
        "address1": "100 Example Street",
        "address2": "Suite 200",
        "address_family_id": null,
        "city": "Springfield",
        "copied_from_address_id": null,
        "country": "United States",
        "country_code": "US",
        "customer_id": "V4keXwwxMQyXw7",
        "external_type": null,
        "hold_stock": false,
        "id": "KKhhlVobndGXEj",
        "is_current_source": true,
        "is_default_warehouse": false,
        "is_external": false,
        "is_primary": true,
        "label": "Head office",
        "nexus": false,
        "note": null,
        "owner_id": "V4keXwwxMQyXw7",
        "owner_type": "customer",
        "postal_can_validate": false,
        "postal_is_valid": false,
        "postal_validated": false,
        "postcode": "62701",
        "revision_number": 1,
        "role": "billing",
        "source": "customer",
        "state": "Illinois",
        "state_code": "IL",
        "superseded_at": null,
        "superseded_by_address_id": null,
        "trashed": false,
        "used_by_active_profile": false
      },
      {
        "address1": "100 Example Street",
        "address2": "Dock 4",
        "address_family_id": null,
        "city": "Springfield",
        "copied_from_address_id": null,
        "country": "United States",
        "country_code": "US",
        "customer_id": "V4keXwwxMQyXw7",
        "external_type": null,
        "hold_stock": false,
        "id": "swtHxyIfOvF62K",
        "is_current_source": true,
        "is_default_warehouse": false,
        "is_external": false,
        "is_primary": true,
        "label": "Warehouse",
        "nexus": false,
        "note": null,
        "owner_id": "V4keXwwxMQyXw7",
        "owner_type": "customer",
        "postal_can_validate": false,
        "postal_is_valid": false,
        "postal_validated": false,
        "postcode": "62701",
        "revision_number": 1,
        "role": "shipping",
        "source": "customer",
        "state": "Illinois",
        "state_code": "IL",
        "superseded_at": null,
        "superseded_by_address_id": null,
        "trashed": false,
        "used_by_active_profile": false
      }
    ],
    "assigned_users": [
      {
        "avatar_url": "https://example.com/avatars/alex.png",
        "id": "lOURnovd3hkIQI",
        "name": "Alex Rivera"
      }
    ],
    "contacts": [
      {
        "created_at": "2026-11-03T09:00:00+00:00",
        "customer_id": "V4keXwwxMQyXw7",
        "default_receive_sms": false,
        "email": "alex@example.com",
        "external_type": null,
        "fax": null,
        "fax_country": null,
        "first_name": "Alex",
        "id": "cBoKHftCid2dDD",
        "is_external": false,
        "is_primary": true,
        "last_name": "Rivera",
        "phone": "+1 201 555 0123",
        "phone_country": "US",
        "send_invoice": true,
        "trashed": false,
        "updated_at": "2026-11-03T09:00:00+00:00"
      }
    ],
    "created_at": "2026-11-03T09:00:00+00:00",
    "customer_portal_enabled": null,
    "customer_portal_id": "HWYGf6vzKsjwpS",
    "customer_portal_id_override": null,
    "customer_portal_link": null,
    "customer_portal_payment_option": null,
    "customer_profiles": [],
    "default_payment_term_id": null,
    "default_pricing_group_id": null,
    "default_warehouse_id": null,
    "deposit_due_amount": 0,
    "display_customer_portal_id": "HWYGf6vzKsjwpS",
    "external_type": null,
    "has_customer_portal_id_override": false,
    "held_credit": 0,
    "hide": false,
    "id": "V4keXwwxMQyXw7",
    "internal_notes": null,
    "is_editable": true,
    "is_external": false,
    "is_system": false,
    "last_ordered_date": null,
    "media": [],
    "name": "Northwind Apparel",
    "ordered_amount": 0,
    "overdue_amount": 0,
    "pdf": {
      "invoice_statement": "https://example.com/pdf/customer_invoice_statement/V4keXwwxMQyXw7"
    },
    "quote_amount": 0,
    "resale_number": null,
    "shopify_compliance_deletion": null,
    "system_type": null,
    "tags": [],
    "tax_exempt": false,
    "third_party_billing_default": "none",
    "trashed": false,
    "unpaid_amount": 0,
    "updated_at": "2026-11-03T09:00:00+00:00",
    "vat_number": null,
    "wallet_currency": {
      "default": true,
      "id": "icHZvqV1nkXRcA",
      "iso": "USD",
      "name": "US Dollar",
      "symbol": "$",
      "trashed": false
    },
    "wallet_fund": 0,
    "website": "https://example.com"
  },
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
  - When: Your role cannot create customers. This 403 has a short body, `{"message": "Insufficient Permission"}`, with no `success` or `error`. Fix: Ask a team owner for the Manage Customers permission.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
- `409`
  - When: Two contacts in the request have the same email, ignoring case and surrounding spaces (`error.code` `customer_portal_duplicate_contact_email`). Nothing is saved. Fix: Send each email once.
- `422` The request was refused and changed nothing. `validation_failed` lists what to fix in `error.fields`; other codes name a business rule, explained in `error.message`. A few checks answer with only `{"message": "..."}`. An operation that calls another service (a payment provider, a carrier, an accounting app) can answer `connect_timeout` ("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.
  - When: A required field is missing, a phone is not valid for its country, a US or Canadian state can't be matched, or two items in one list are primary. `error.code` is `validation_failed`. Fix: Fix each field named in `error.fields`; its keys are paths such as `contact.0.first_name` or `billing` (for two primaries).
  - When: On a live customer, the email is longer than 191 characters or is not a valid address (`error.code` `customer_portal_email_invalid`, "Enter a valid email address using supported characters."). Fix: Send a valid email of at most 191 characters.

## Notes

- A field sent as `null` is dropped before the request is read, except `customer.default_payment_term_id`, `customer.customer_portal_payment_option` and `customer.customer_portal_enabled`, which are saved as null.

## Related

- [Create or update a customer by contact](https://developer.yoprint.com/reference/customers/v1-customer-upsert.md): `POST /v1/api/store/{team_slug}/customer/upsert`
- [Update a customer (PATCH)](https://developer.yoprint.com/reference/customers/v1-customer-update-patch.md): `PATCH /v1/api/store/{team_slug}/customer/{hash_id}`
- [Add a contact to a customer](https://developer.yoprint.com/reference/customer-contacts/v1-customer-contact-store.md): `POST /v1/api/store/{team_slug}/customer/{hash_id}/contact`
- [Add an address to a customer](https://developer.yoprint.com/reference/customer-addresses/v1-customer-address-store.md): `POST /v1/api/store/{team_slug}/customer/{hash_id}/address`
- [List payment terms](https://developer.yoprint.com/reference/payment-terms/v1-payment-term-index.md): `GET /v1/api/store/{team_slug}/setting/payment_term`
