> ## Documentation Index
> Fetch the complete documentation index at: https://docs.horizonpay.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Customers API — List, Create, Retrieve, Update, Delete

> Manage the customers you bill. Create, update, and archive customer records. All amounts on related invoices use integer minor units.

# Customers

A **customer** represents a person or business you issue invoices to. Every invoice must be linked to a customer. Deleting a customer is a soft-delete — the record is archived (`deleted: true`) and no longer appears in list results by default, but historical invoices retain full customer details.

**Base URL:** `https://invoice.horizonpay.co/api/v1`

***

## The customer object

```json theme={null}
{
  "id": "cus_01hxyz1234567890abcdefghij",
  "object": "customer",
  "name": "Acme Corp",
  "email": "billing@acme.com",
  "phone": "+1-555-867-5309",
  "tax_id": "US-12-3456789",
  "address": {
    "line1": "742 Evergreen Terrace",
    "line2": "Suite 100",
    "city": "Springfield",
    "state": "IL",
    "postal_code": "62701",
    "country": "US"
  },
  "deleted": false,
  "created": "2024-03-15T09:22:00.000Z"
}
```

<ResponseField name="id" type="string">
  Unique identifier for the customer. Prefixed with `cus_`.
</ResponseField>

<ResponseField name="object" type="string">
  String literal `"customer"`.
</ResponseField>

<ResponseField name="name" type="string">
  Full legal or trade name of the customer.
</ResponseField>

<ResponseField name="email" type="string | null">
  Contact email address. Used when sending invoice emails directly to this customer.
</ResponseField>

<ResponseField name="phone" type="string | null">
  Phone number in any format.
</ResponseField>

<ResponseField name="tax_id" type="string | null">
  Tax identifier (VAT number, EIN, GSTIN, ABN, etc.). No format validation is applied — store whatever format the customer uses.
</ResponseField>

<ResponseField name="address" type="object">
  Billing address.

  <Expandable title="address fields">
    <ResponseField name="line1" type="string | null">Street address, line 1.</ResponseField>
    <ResponseField name="line2" type="string | null">Apartment, suite, unit, floor, etc.</ResponseField>
    <ResponseField name="city" type="string | null">City or locality.</ResponseField>
    <ResponseField name="state" type="string | null">State, province, or region.</ResponseField>
    <ResponseField name="postal_code" type="string | null">ZIP or postal code.</ResponseField>
    <ResponseField name="country" type="string | null">Two-letter ISO 3166-1 alpha-2 country code (e.g. `"US"`).</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="deleted" type="boolean">
  `true` when the customer has been archived. Archived customers no longer appear in list results unless `include_deleted=true` is passed.
</ResponseField>

<ResponseField name="created" type="string">
  ISO 8601 datetime at which the customer was created.
</ResponseField>

***

## Endpoints

<AccordionGroup>
  <Accordion title="GET /api/v1/customers — List customers">
    List all customers belonging to your workspace. Results are cursor-paginated, newest first.

    **Required scope:** `clients:read`

    ### Query parameters

    <ParamField query="query" type="string">
      Free-text search across name, email, and tax ID. Partial matches are supported.
    </ParamField>

    <ParamField query="cursor" type="string">
      Pagination cursor returned as `next_cursor` from a previous response. Omit to start from the beginning.
    </ParamField>

    <ParamField query="limit" type="integer">
      Maximum number of customers to return. Defaults to `20`; maximum is `100`.
    </ParamField>

    <ParamField query="include_deleted" type="boolean">
      When `true`, archived (soft-deleted) customers are included in results. Defaults to `false`.
    </ParamField>

    ### Request

    ```bash theme={null}
    curl https://invoice.horizonpay.co/api/v1/customers?limit=2 \
      -H "Authorization: Bearer inv_live_..."
    ```

    ### Response

    ```json theme={null}
    {
      "data": [
        {
          "id": "cus_01hxyz1234567890abcdefghij",
          "object": "customer",
          "name": "Acme Corp",
          "email": "billing@acme.com",
          "phone": "+1-555-867-5309",
          "tax_id": "US-12-3456789",
          "address": {
            "line1": "742 Evergreen Terrace",
            "line2": "Suite 100",
            "city": "Springfield",
            "state": "IL",
            "postal_code": "62701",
            "country": "US"
          },
          "deleted": false,
          "created": "2024-03-15T09:22:00.000Z"
        },
        {
          "id": "cus_01hxyz9876543210zyxwvutsrq",
          "object": "customer",
          "name": "Globex Corporation",
          "email": null,
          "phone": null,
          "tax_id": null,
          "address": {
            "line1": null,
            "line2": null,
            "city": null,
            "state": null,
            "postal_code": null,
            "country": null
          },
          "deleted": false,
          "created": "2024-02-01T14:05:30.000Z"
        }
      ],
      "next_cursor": "cus_01hxyz9876543210zyxwvutsrq"
    }
    ```

    Pass the returned `next_cursor` value as the `cursor` query parameter on your next request to retrieve the following page. When `next_cursor` is `null`, you have reached the last page.
  </Accordion>

  <Accordion title="POST /api/v1/customers — Create a customer">
    Create a new customer. Only `name` is required; all other fields are optional.

    **Required scope:** `clients:write`

    Optionally send an `Idempotency-Key` header to safely retry the request without creating duplicates.

    ### Body parameters

    <ParamField body="name" type="string" required>
      Full name or business name. Minimum 1 character.
    </ParamField>

    <ParamField body="email" type="string">
      Contact email. Must be a valid email address if provided.
    </ParamField>

    <ParamField body="phone" type="string">
      Phone number in any format.
    </ParamField>

    <ParamField body="tax_id" type="string">
      Tax identifier (VAT, EIN, GSTIN, ABN, etc.).
    </ParamField>

    <ParamField body="address" type="object">
      Billing address object.

      <Expandable title="address fields">
        <ParamField body="line1" type="string">Street address, line 1.</ParamField>
        <ParamField body="line2" type="string">Suite, floor, unit, etc.</ParamField>
        <ParamField body="city" type="string">City or locality.</ParamField>
        <ParamField body="state" type="string">State, province, or region.</ParamField>
        <ParamField body="postal_code" type="string">ZIP or postal code.</ParamField>
        <ParamField body="country" type="string">Two-letter ISO 3166-1 alpha-2 country code (e.g. `"US"`).</ParamField>
      </Expandable>
    </ParamField>

    ### Request

    ```bash theme={null}
    curl https://invoice.horizonpay.co/api/v1/customers \
      -X POST \
      -H "Authorization: Bearer inv_live_..." \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: create-acme-2024-03-15" \
      -d '{
        "name": "Acme Corp",
        "email": "billing@acme.com",
        "phone": "+1-555-867-5309",
        "tax_id": "US-12-3456789",
        "address": {
          "line1": "742 Evergreen Terrace",
          "line2": "Suite 100",
          "city": "Springfield",
          "state": "IL",
          "postal_code": "62701",
          "country": "US"
        }
      }'
    ```

    ### Response `201 Created`

    ```json theme={null}
    {
      "data": {
        "id": "cus_01hxyz1234567890abcdefghij",
        "object": "customer",
        "name": "Acme Corp",
        "email": "billing@acme.com",
        "phone": "+1-555-867-5309",
        "tax_id": "US-12-3456789",
        "address": {
          "line1": "742 Evergreen Terrace",
          "line2": "Suite 100",
          "city": "Springfield",
          "state": "IL",
          "postal_code": "62701",
          "country": "US"
        },
        "deleted": false,
        "created": "2024-03-15T09:22:00.000Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="GET /api/v1/customers/:id — Retrieve a customer">
    Retrieve a single customer by ID.

    **Required scope:** `clients:read`

    The `:id` path parameter accepts either the `cus_`-prefixed public ID or the underlying UUID.

    ### Request

    ```bash theme={null}
    curl https://invoice.horizonpay.co/api/v1/customers/cus_01hxyz1234567890abcdefghij \
      -H "Authorization: Bearer inv_live_..."
    ```

    ### Response `200 OK`

    ```json theme={null}
    {
      "data": {
        "id": "cus_01hxyz1234567890abcdefghij",
        "object": "customer",
        "name": "Acme Corp",
        "email": "billing@acme.com",
        "phone": "+1-555-867-5309",
        "tax_id": "US-12-3456789",
        "address": {
          "line1": "742 Evergreen Terrace",
          "line2": "Suite 100",
          "city": "Springfield",
          "state": "IL",
          "postal_code": "62701",
          "country": "US"
        },
        "deleted": false,
        "created": "2024-03-15T09:22:00.000Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="PATCH /api/v1/customers/:id — Update a customer">
    Update one or more fields on an existing customer. Only the fields you include are changed; omitted fields retain their current values.

    **Required scope:** `clients:write`

    ### Body parameters

    All fields are optional. Include only those you want to change.

    <ParamField body="name" type="string">
      New name for the customer.
    </ParamField>

    <ParamField body="email" type="string">
      New email address. Pass an empty string `""` to clear the field.
    </ParamField>

    <ParamField body="phone" type="string">
      New phone number.
    </ParamField>

    <ParamField body="tax_id" type="string">
      New tax identifier.
    </ParamField>

    <ParamField body="address" type="object">
      Partial or full address update. Only keys you include are updated within the address object.

      <Expandable title="address fields">
        <ParamField body="line1" type="string">Street address, line 1.</ParamField>
        <ParamField body="line2" type="string">Suite, floor, unit, etc.</ParamField>
        <ParamField body="city" type="string">City or locality.</ParamField>
        <ParamField body="state" type="string">State, province, or region.</ParamField>
        <ParamField body="postal_code" type="string">ZIP or postal code.</ParamField>
        <ParamField body="country" type="string">Two-letter ISO 3166-1 alpha-2 country code.</ParamField>
      </Expandable>
    </ParamField>

    ### Request

    ```bash theme={null}
    curl https://invoice.horizonpay.co/api/v1/customers/cus_01hxyz1234567890abcdefghij \
      -X PATCH \
      -H "Authorization: Bearer inv_live_..." \
      -H "Content-Type: application/json" \
      -d '{
        "email": "ap@acme.com",
        "address": {
          "line2": "Floor 3"
        }
      }'
    ```

    ### Response `200 OK`

    ```json theme={null}
    {
      "data": {
        "id": "cus_01hxyz1234567890abcdefghij",
        "object": "customer",
        "name": "Acme Corp",
        "email": "ap@acme.com",
        "phone": "+1-555-867-5309",
        "tax_id": "US-12-3456789",
        "address": {
          "line1": "742 Evergreen Terrace",
          "line2": "Floor 3",
          "city": "Springfield",
          "state": "IL",
          "postal_code": "62701",
          "country": "US"
        },
        "deleted": false,
        "created": "2024-03-15T09:22:00.000Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="DELETE /api/v1/customers/:id — Archive a customer">
    Soft-delete (archive) a customer. The record is not destroyed — all historical invoices retain their customer references. The customer will no longer appear in list results unless you pass `include_deleted=true`.

    **Required scope:** `clients:write`

    This action mirrors Stripe's `deleted: true` convention. Archived customers still appear when you retrieve them by ID or pass `include_deleted=true` to the list endpoint.

    ### Request

    ```bash theme={null}
    curl https://invoice.horizonpay.co/api/v1/customers/cus_01hxyz1234567890abcdefghij \
      -X DELETE \
      -H "Authorization: Bearer inv_live_..."
    ```

    ### Response `200 OK`

    The archived customer object is returned, with `deleted: true`.

    ```json theme={null}
    {
      "data": {
        "id": "cus_01hxyz1234567890abcdefghij",
        "object": "customer",
        "name": "Acme Corp",
        "email": "billing@acme.com",
        "phone": "+1-555-867-5309",
        "tax_id": "US-12-3456789",
        "address": {
          "line1": "742 Evergreen Terrace",
          "line2": "Suite 100",
          "city": "Springfield",
          "state": "IL",
          "postal_code": "62701",
          "country": "US"
        },
        "deleted": true,
        "created": "2024-03-15T09:22:00.000Z"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Error responses

| Status             | Cause                                                                                                                                                               |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`  | Validation failed — missing `name`, invalid email format, or unrecognised country code. The response body includes a `detail` field and a per-field `errors` array. |
| `401 Unauthorized` | Missing or invalid `Authorization` header.                                                                                                                          |
| `403 Forbidden`    | The API key lacks the required scope (`clients:read` or `clients:write`).                                                                                           |
| `404 Not Found`    | No customer with the given ID exists in your workspace.                                                                                                             |
| `409 Conflict`     | An `Idempotency-Key` was reused with a different request body.                                                                                                      |
