# Account setup

Resolve references and create complete customer and supplier profiles with source-faithful records.

Canonical HTML: https://developer.shelfcycle.com/guides/account-setup

Set up complete customer and supplier accounts by resolving shared business references first, preserving source IDs, and recording every result so the workflow can resume safely.

This sequence keeps commercial account setup connected from the first source record through verified ShelfCycle readback.

## 1. Verify access and currency

Call `GET /me` before reading references or creating accounts. Confirm:

- `apiContractVersion` supports `v1.42` and `minimumCompatibleContractVersion` supports your client.
- `data.org.baseCurrencyCode` matches any credit-limit currency you send.
- `company-reference:read` is available for reference directories.
- `company-reference:write` is available when the workflow creates or maintains representatives, payment terms, or Chart of Accounts records.
- `customers:write`, `suppliers:write`, `contacts:create`, or `notes:write` are available for the records the workflow will create.
- `effectiveCapabilities.companyReference` and the matching customer or supplier capabilities show the actions currently available.

```bash
curl "$SHELFCYCLE_API_BASE_URL/me" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "X-ShelfCycle-Client: Account setup"
```

Scopes and capabilities confirm general access. Each request still checks the selected record and the acting person's current access.

## 2. Resolve shared references first

Resolve references before creating a customer or supplier so the profile can be complete in one request.

| Reference | Read endpoint | Identity |
| --- | --- | --- |
| Representatives | `GET /representatives` | `kind` plus `id` or source `externalId` |
| Payment terms | `GET /payment-terms` | `id`, exact `name`, or source `externalId` |
| Supplier cost account | `GET /reference/ledger-accounts` | `id` or source `externalId` |

Representative kinds are exact:

```text
salesperson
customer_service
supplier
```

The same source ID can identify different representative kinds, so always store the kind with the returned ID. `email` and `phone` are available when creating or updating a `salesperson`; customer-service and supplier representatives use their name and source ID fields.

```bash
curl "$SHELFCYCLE_API_BASE_URL/representatives?kind=salesperson&externalId=CRM-REP-42" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

curl "$SHELFCYCLE_API_BASE_URL/payment-terms?externalId=ERP-TERM-N30" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

curl "$SHELFCYCLE_API_BASE_URL/reference/ledger-accounts?externalId=GL-COGS-100" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"
```

Continue a larger directory with its returned `meta.nextCursor`. Use the cursor by itself with the same endpoint.

## 3. Create a missing representative, payment term, or ledger account

Use `company-reference:write` when an exact source reference does not exist and the workflow has the matching current access.

```bash
curl "$SHELFCYCLE_API_BASE_URL/representatives" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm:rep-42:create-salesperson" \
  -d '{
    "kind": "salesperson",
    "firstName": "Jordan",
    "lastName": "Lee",
    "externalId": "CRM-REP-42",
    "email": "jordan@example.com",
    "phone": "555-0199"
  }'
```

Create payment terms with their complete financial definition:

```bash
curl "$SHELFCYCLE_API_BASE_URL/payment-terms" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: erp:term-n30:create" \
  -d '{
    "name": "Net 30",
    "description": "Payment due 30 days after invoice date.",
    "externalId": "ERP-TERM-N30",
    "netDays": 30,
    "discountDays": null,
    "discountPercentage": null
  }'
```

`netDays`, `discountDays`, and `discountPercentage` are set when the payment term is created. `discountPercentage` is an exact decimal string from 0 through 100 with at most two decimal places. Use `PATCH /payment-terms/{id}` only for `name`, `description`, or `externalId`, with the latest `updatedAt` in `If-Match`.

Create a missing supplier cost account before building the supplier profile:

```bash
curl "$SHELFCYCLE_API_BASE_URL/reference/ledger-accounts" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Freight Expense",
    "accountType": "EXPENSE",
    "externalId": "GL-FREIGHT-6100",
    "incomeStatementType": "isOrdinary"
  }'
```

Preview with `dryRun=true` when the workflow reviews changes before applying them. Save the returned account ID for `costLedgerAccountId`. See [Chart of Accounts](/guides/chart-of-accounts) for account types, classification, updates, and readback.

Representatives can be updated, archived, or restored through `/representatives/{kind}/{id}` and its `/archive` or `/unarchive` action. Keep the kind in every path. Representative updates preserve any linked ShelfCycle user.

## 4. Create a complete customer

Customer commercial details belong to the primary location. Resolve a `salesperson` for `accountOwnerId`, a `customer_service` representative for `customerServiceRepresentativeId`, and payment terms before creating the profile.

```bash
curl "$SHELFCYCLE_API_BASE_URL/customers" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm:customer-1842:create" \
  -d '{
    "name": "Northstar Chemical",
    "externalId": "CRM-CUSTOMER-1842",
    "pipelineStatus": "ACTIVE",
    "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
      }
    }
  }'
```

Credit uses integer `amountCents`. If `currencyCode` is present, it must match the organization base currency returned by `/me`. A zero limit is different from clearing the value with `null`.

Additional customer locations can carry their own `accountOwnerId`, `paymentTermsId`, `customerServiceRepresentativeId`, and `creditLimit` through `POST /customers/{id}/locations` or `PATCH /customers/{id}/locations/{locationId}`.

## 5. Create a complete supplier

Resolve payment terms, a `supplier` representative, and an eligible ledger account before sending the supplier profile.

```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 Houston 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`. Eligible cost accounts can have account type `ASSET`, `LIABILITY`, `EQUITY`, `REVENUE`, or `EXPENSE`. Results also include `excludeFromReports`, `isCogs`, and `isNonOperational` so the workflow can preserve the selected account's meaning.

## 6. Preserve source identity

Keep each source system's stable record ID in `externalId`. Use a separate `Idempotency-Key` to identify the create request and its retries.

```text
externalId: the source record's durable identity
Idempotency-Key: the durable identity of this create request
```

Customers, suppliers, contacts, notes, customer addresses, and supplier ship-from locations accept `externalId`. Save the returned ShelfCycle ID and `updatedAt` alongside that source ID.

When a supported create resembles an existing record but is a separate legitimate source record, add this top-level field:

```json
{
  "duplicateResolution": "create_distinct"
}
```

This explicit intent is available on customer, supplier, contact, combined customer-location, customer-address, and supplier ship-from creates. It permits a separate record only for reviewable similarity matches. Exact source-ID conflicts and other identity conflicts still require correction. Omit the field when the new request should match or reuse an existing record.

## 7. Record outcomes and resume safely

For every request, record the request key, source ID, endpoint, response status, returned ShelfCycle ID, `idempotencyStatus`, `updatedAt`, and any repair details.

- If the response is unknown after an interruption, retry the identical body with the same `Idempotency-Key`.
- If `idempotencyStatus` is `replayed`, use the returned current record without creating another one.
- If a request is blocked or fails, store the error and repair fields before moving to the next independent record.
- Respect each endpoint's rate-limit headers. Wait for `Retry-After` before retrying a rate-limited request.
- Use the latest returned `updatedAt` as `If-Match` for a PATCH. Read the record again before retrying a stale update.

`dryRun=true` is optional on supported writes. When used, inspect `data.status`, `checks`, `duplicateCandidates`, and `impactAnalysis`; HTTP 200 can still describe a blocked request.

## 8. Verify before continuing

Read back each created or updated record and compare the stored fields with the source record:

```text
GET /customers/{id}
GET /suppliers/{id}
GET /contacts/{id}
GET /notes/{id}
GET /customers/{id}/locations/{locationId}/addresses/{addressId}
GET /suppliers/{id}/ship-from-locations/{locationId}
GET /representatives/{kind}/{id}
GET /payment-terms/{id}
```

Verify source ID, commercial references, credit amount and currency, address identity, and the latest `updatedAt`. Keep unresolved or unsupported source fields in the workflow's exception list instead of treating a created row as complete.

> **Guardrail**: Stop for an ambiguous match, an exact source-ID conflict, a missing reference, a currency mismatch, or a source field that has no documented destination. Correct the source-to-target mapping before continuing.
