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.
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.

ReferenceRead endpointIdentity
RepresentativesGET /representativeskind plus id or source externalId
Payment termsGET /payment-termsid, exact name, or source externalId
Supplier cost accountGET /reference/ledger-accountsid or source externalId

Representative kinds are exact:

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.

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.

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:

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:

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 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.

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.

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.

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:

{
  "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:

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.