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

# Manage customers

> Create and manage customer profiles to attach payment methods, subscriptions, and invoices.

A customer (`cus_…`) is a stable identity stored under your merchant account. Attach payment methods to a customer to enable off-session charges, subscriptions, and invoice delivery from a single profile.

On Node, `npm install @ionicfi/sdk` gives you typed calls for every request
below; the [server SDK](/sdks/server) guide has details. Each example also
shows raw curl.

## Create a customer

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Ionic } from "@ionicfi/sdk";

  const ionic = new Ionic({ token: process.env.IONIC_SECRET_KEY });

  const customer = await ionic.customers.create({
    "Idempotency-Key": "create_cus_jane_doe",
    email: "jane@example.com",
    first_name: "Jane",
    last_name: "Doe",
    phone: "+14155552671",
    billing_address: {
      line1: "123 Main St",
      city: "San Francisco",
      state: "CA",
      postal_code: "94103",
      country: "US",
    },
    metadata: {
      plan: "premium",
    },
  });
  ```

  ```bash curl theme={null}
  curl https://api.ionicfi.com/v1/customers \
    -H "Authorization: Bearer sk_v1_test_..." \
    -H "Idempotency-Key: create_cus_jane_doe" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "jane@example.com",
      "first_name": "Jane",
      "last_name": "Doe",
      "phone": "+14155552671",
      "billing_address": {
        "line1": "123 Main St",
        "city": "San Francisco",
        "state": "CA",
        "postal_code": "94103",
        "country": "US"
      },
      "metadata": {
        "plan": "premium"
      }
    }'
  ```
</CodeGroup>

Email must be unique per merchant per mode. `billing_address` and `shipping` are optional and can be added or updated after creation.

### Request fields

| Field             | Type   | Notes                                                                                   |
| ----------------- | ------ | --------------------------------------------------------------------------------------- |
| `email`           | string | Required. Unique per merchant and mode.                                                 |
| `first_name`      | string |                                                                                         |
| `last_name`       | string |                                                                                         |
| `phone`           | string |                                                                                         |
| `billing_address` | object | Nullable. Requires `line1`, `city`, `state`, `postal_code`, and `country` when present. |
| `shipping`        | object | Nullable. Requires `name` and `address`; `phone` is optional.                           |
| `creation_source` | string | Defaults to `api` when omitted.                                                         |
| `metadata`        | object | Up to 50 key-value pairs. Keys ≤ 40 characters; values ≤ 500 characters.                |

## Retrieve a customer

<CodeGroup>
  ```ts TypeScript theme={null}
  const retrieved = await ionic.customers.retrieve({ id: "cus_2pXqAbc" });
  ```

  ```bash curl theme={null}
  curl https://api.ionicfi.com/v1/customers/cus_2pXqAbc \
    -H "Authorization: Bearer sk_v1_test_..."
  ```
</CodeGroup>

The retrieve endpoint returns the full customer object plus dashboard detail: all payment methods on file (`payment_methods`), recent payments (`recent_payments`), and a lifetime spend summary (`summary`) with counts and amounts by currency. Use the `section_size` query parameter to cap the number of items returned in `payment_methods` and `recent_payments` (1–100).

## Update a customer

<CodeGroup>
  ```ts TypeScript theme={null}
  const updated = await ionic.customers.update({
    id: "cus_2pXqAbc",
    "Idempotency-Key": "update_cus_jane_smith",
    last_name: "Smith",
    phone: "+14155552672",
    metadata: {
      plan: "enterprise",
    },
  });
  ```

  ```bash curl theme={null}
  curl -X POST https://api.ionicfi.com/v1/customers/cus_2pXqAbc \
    -H "Authorization: Bearer sk_v1_test_..." \
    -H "Idempotency-Key: update_cus_jane_smith" \
    -H "Content-Type: application/json" \
    -d '{
      "last_name": "Smith",
      "phone": "+14155552672",
      "metadata": {
        "plan": "enterprise"
      }
    }'
  ```
</CodeGroup>

All body fields are optional. Only fields present in the body are changed. Pass `null` for `billing_address` or `shipping` to remove the address. Updating a deleted customer returns `422`.

### Update fields

| Field             | Type           | Notes                                         |
| ----------------- | -------------- | --------------------------------------------- |
| `email`           | string         | Must be unique per merchant and mode.         |
| `first_name`      | string         |                                               |
| `last_name`       | string         |                                               |
| `phone`           | string         |                                               |
| `billing_address` | object or null | Pass `null` to clear the billing address.     |
| `shipping`        | object or null | Pass `null` to clear shipping details.        |
| `metadata`        | object         | Replaces the customer's metadata map in full. |

## List customers

<CodeGroup>
  ```ts TypeScript theme={null}
  const customers = await ionic.customers.list({ limit: 20 });
  ```

  ```bash curl theme={null}
  curl "https://api.ionicfi.com/v1/customers?limit=20" \
    -H "Authorization: Bearer sk_v1_test_..."
  ```
</CodeGroup>

The list returns customers newest first. Each item includes a lifetime payment summary (`summary`), the customer's default payment method, and their most recent payment. Full `payment_methods` and `recent_payments` arrays are not included in list items — use the retrieve endpoint for those.

When `has_more` is true, the response includes `next_cursor`, the last
returned customer's id. Pass it as `starting_after` to fetch the next page;
iterating the SDK's list with `for await` does this automatically.

<CodeGroup>
  ```ts TypeScript theme={null}
  const nextPage = await ionic.customers.list({
    limit: 20,
    starting_after: "cus_2pXqAbc",
  });
  ```

  ```bash curl theme={null}
  curl "https://api.ionicfi.com/v1/customers?limit=20&starting_after=cus_2pXqAbc" \
    -H "Authorization: Bearer sk_v1_test_..."
  ```
</CodeGroup>

The list also accepts `offset` for offset-based pagination, `search` to filter by email or name prefix, `created_gte` and `created_lte` as RFC 3339 timestamps, and `include_deleted=true` to include soft-deleted records.

## Attach a payment method

Attach a vaulted payment method to a customer to make it available for off-session charges and subscriptions.

<CodeGroup>
  ```ts TypeScript theme={null}
  await ionic.paymentMethods.attach({
    id: "pm_abc123",
    "Idempotency-Key": "attach_pm_abc123_cus_2pXqAbc",
    customer: "cus_2pXqAbc",
  });
  ```

  ```bash curl theme={null}
  curl -X POST https://api.ionicfi.com/v1/payment_methods/pm_abc123/attach \
    -H "Authorization: Bearer sk_v1_test_..." \
    -H "Idempotency-Key: attach_pm_abc123_cus_2pXqAbc" \
    -H "Content-Type: application/json" \
    -d '{
      "customer": "cus_2pXqAbc"
    }'
  ```
</CodeGroup>

The method must be `active` and not already attached to a customer. Attachment is write-once. The `customer` field on the payment method response reflects the attached customer id after the call.

To make the attached method the customer's default, set it explicitly. Pass
`null` for `default_payment_method` to clear the default.

<CodeGroup>
  ```ts TypeScript theme={null}
  const withDefault = await ionic.customers.setDefaultPaymentMethod({
    id: "cus_2pXqAbc",
    "Idempotency-Key": "set_default_pm_abc123",
    default_payment_method: "pm_abc123",
  });
  ```

  ```bash curl theme={null}
  curl -X POST https://api.ionicfi.com/v1/customers/cus_2pXqAbc/default_payment_method \
    -H "Authorization: Bearer sk_v1_test_..." \
    -H "Idempotency-Key: set_default_pm_abc123" \
    -H "Content-Type: application/json" \
    -d '{
      "default_payment_method": "pm_abc123"
    }'
  ```
</CodeGroup>

The `payment_settings.default_payment_method` field on the customer response always reflects the current default. Subscriptions and invoices use this method for automatic collection when no payment method is specified at creation time.

## Delete and restore

Deleting a customer is a soft-delete. The record is retained with `deleted: true` and cannot be modified until restored.

<CodeGroup>
  ```ts TypeScript theme={null}
  await ionic.customers.delete({ id: "cus_2pXqAbc" });
  ```

  ```bash curl theme={null}
  curl -X DELETE https://api.ionicfi.com/v1/customers/cus_2pXqAbc \
    -H "Authorization: Bearer sk_v1_test_..."
  ```
</CodeGroup>

To undo the deletion:

<CodeGroup>
  ```ts TypeScript theme={null}
  await ionic.customers.restore({ id: "cus_2pXqAbc" });
  ```

  ```bash curl theme={null}
  curl -X POST https://api.ionicfi.com/v1/customers/cus_2pXqAbc/restore \
    -H "Authorization: Bearer sk_v1_test_..."
  ```
</CodeGroup>

A `customer.deleted` webhook fires on deletion and a `customer.restored` webhook fires on restore. See [webhook events](/webhooks/events) for the full event catalog.

<Tip>
  Pass `include_deleted=true` on the list endpoint to include soft-deleted customers in results. Deleted customers are excluded by default.
</Tip>

## Operational guidance

* Email must be unique per merchant per mode. A `409` with code `CUSTOMER_EMAIL_ALREADY_EXISTS` means a record already exists for that email. Use the `search` parameter on the list endpoint to locate it rather than creating a duplicate.
* The `metadata` map is replaced in full on every update. Read the current values before writing if you need to preserve existing keys.
* Attach a payment method before creating subscriptions or off-session payment intents. An operation that targets `payment_settings.default_payment_method` requires the attachment to be in place at the time of the call.
* Soft-delete customers instead of abandoning records. Deleted customers retain their payment method and invoice history and can be restored when a customer reactivates.
* Use the same `Idempotency-Key` on every retry for create, update, and attach calls. A unique key per logical operation prevents duplicates from network retries.
