# Company profiles

Create, update, archive, and restore customer and supplier profiles.

Canonical HTML: https://developer.shelfcycle.com/guides/company-profiles

Create and update complete customer and supplier profiles with the commercial setup needed for connected onboarding.

## Supported writes

| Endpoint | Scope | Required header |
| --- | --- | --- |
| `POST /customers` | `customers:write` | `Idempotency-Key` |
| `PATCH /customers/{id}` | `customers:write` | `If-Match` |
| `POST /customers/{id}/archive` or `/unarchive` | `customers:write` | `If-Match` |
| `POST /suppliers` | `suppliers:write` | `Idempotency-Key` |
| `PATCH /suppliers/{id}` | `suppliers:write` | `If-Match` |
| `POST /suppliers/{id}/archive` or `/unarchive` | `suppliers:write` | `If-Match` |

POST requests require a stable idempotency key. PATCH requests require the latest `updatedAt` value as `If-Match` and preserve omitted fields. Add `dryRun=true` to the same endpoint when your workflow needs a non-mutating readiness check before execution.

## Customer profiles

Customer profile writes combine identity with an initial primary location, billing or shipping address, account owner, payment terms, customer-service representative, and credit limit. Resolve the referenced records first through the company reference directories. Use the locations and addresses endpoints for additional customer locations or addresses after the customer exists.

```bash
curl "$SHELFCYCLE_API_BASE_URL/customers" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm:lead-1842:create-customer" \
  -d '{
    "name": "Northstar Chemical",
    "externalId": "NS-1842",
    "pipelineStatus": "PROSPECT",
    "primaryLocation": {
      "name": "Northstar Chemical HQ",
      "email": "ops@northstar.example",
      "phone": "555-0100",
      "accountOwnerId": "salesperson-id",
      "paymentTermsId": "payment-term-id",
      "customerServiceRepresentativeId": "customer-service-id",
      "creditLimit": {
        "amountCents": 2500000,
        "currencyCode": "USD"
      },
      "address": {
        "name": "Northstar Chemical HQ",
        "externalId": "CRM-ADDRESS-1842-HQ",
        "street1": "100 Market Street",
        "city": "Chicago",
        "state": "IL",
        "zip": "60601",
        "country": "US",
        "isBilling": true,
        "isShipping": true
      }
    }
  }'
```

Customer locations carry commercial details independently. `accountOwnerId` selects a `salesperson` representative, `customerServiceRepresentativeId` selects a `customer_service` representative, and `paymentTermsId` selects a payment-term record. Credit uses integer `amountCents`; an optional `currencyCode` must match the organization base currency returned by `/me`.

## Supplier profiles

Supplier profile writes combine identity and contact details with tax ID, payment terms, credit limit, preferred unit of measure, an eligible cost ledger account, a supplier representative, and an optional source-identified ship-from address. Use the supplier ship-from endpoints for additional locations after the supplier exists.

```bash
curl "$SHELFCYCLE_API_BASE_URL/suppliers" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: erp:supplier-902:create" \
  -d '{
    "name": "Acme Supply",
    "externalId": "ERP-SUPPLIER-902",
    "email": "orders@acme.example",
    "taxId": "12-3456789",
    "paymentTermsId": "payment-term-id",
    "creditLimit": { "amountCents": 1000000, "currencyCode": "USD" },
    "preferredUnitOfMeasure": "Imperial",
    "costLedgerAccountId": "ledger-account-id",
    "supplierRepresentativeId": "supplier-representative-id",
    "primaryShipFromAddress": {
      "name": "Acme Supply Warehouse",
      "externalId": "ERP-SHIPFROM-HOU",
      "street1": "20 Industrial Way",
      "city": "Houston",
      "state": "TX",
      "zip": "77002",
      "country": "US",
      "defaultSupplierShippingInstructions": "Call before pickup."
    }
  }'
```

`preferredUnitOfMeasure` accepts `NoPreference`, `Metric`, or `Imperial`. Select `costLedgerAccountId` from `GET /reference/ledger-accounts`, `paymentTermsId` from `GET /payment-terms`, and `supplierRepresentativeId` from a `supplier` row returned by `GET /representatives`.

## Source IDs and separate records

Customer, supplier, nested customer-address, and nested supplier ship-from creates accept source `externalId` values. Keep each `externalId` separate from the request's `Idempotency-Key`: the first identifies the business record, while the second identifies the create request and its retries.

When a supported create resembles an existing profile but represents a separate legitimate source record, add top-level `duplicateResolution: "create_distinct"`. Omission keeps the normal duplicate check. Exact source-ID conflicts still require correction. See [Account setup](/guides/account-setup) for the complete workflow and all six supported create families.

## Read before update

Use `GET /customers/{id}` or `GET /suppliers/{id}` before a PATCH. The response includes `updatedAt`; send that exact value as `If-Match` so ShelfCycle can reject stale overwrites with `409 stale_record`.

Customer and supplier profiles can also be archived and restored with the same write scope and current user permission. Preview the selected action, then apply it with the latest `updatedAt` value. See [Archive and restore](/guides/archive-restore) for the complete flow and receipt.

> **Guardrail**: Create or update one profile at a time, record every outcome, and stop on exact identity, archived, stale, or access errors.
