# Quickstart

Verify a key, resolve records, preflight, and execute a supported write.

Canonical HTML: https://developer.shelfcycle.com/guides/quickstart

Verify the key, resolve a record, inspect exact context, optionally preflight, and execute one supported write back to ShelfCycle.

This guide assumes you have a ShelfCycle API key. The key is scoped to one tenant and issued to one acting user. Store it locally and read it from the environment at runtime.

> **Guardrail**: Never paste the key into a prompt, chat message, log line, or source control. Give local tooling the environment variable name, not the secret value.

## 1. Configure the environment

Set the key and base URL in a local `.env` file.

```bash
SHELFCYCLE_API_KEY=sk_...
SHELFCYCLE_API_BASE_URL=https://app.shelfcycle.com/api/v1
```

For local ShelfCycle development, use:

```text
http://localhost:3000/api/v1
```

## 2. Verify the key

Call `GET /me` before any workflow step. It returns the route major, contract version, organization, key preview, scopes, acting user, documentation links, and effective capabilities after current ShelfCycle access.

```bash
curl "$SHELFCYCLE_API_BASE_URL/me" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "X-ShelfCycle-Client: Email notes automation"
```

The response should include `apiContractVersion: "v1.44.1"`, the compatibility floor, organization base currency, and the organization, actor, scopes, and capabilities your workflow expects. Compare versions numerically and treat a missing patch as zero; a v1.42.2 client remains compatible when the reported compatibility floor includes it. See [Authentication](/guides/authentication) for the complete compatibility check.

```json
{
  "data": {
    "apiVersion": "v1",
    "apiContractVersion": "v1.44.1",
    "minimumCompatibleContractVersion": "v1.21",
    "org": { "id": "org-id", "name": "Northstar Chemical", "baseCurrencyCode": "USD" },
    "key": {
      "id": "api-key-id",
      "name": "Email notes automation",
      "purpose": "Workflow for creating notes from source emails",
      "preview": "sk_...abcd",
      "scopes": ["company-reference:read", "company-reference:write", "shipping-options:read", "shipping-options:write", "search:read", "tags:read", "tags:write", "warehouses:read", "warehouses:write", "inventory:read", "lots:read", "reorder:read", "reorder:write", "notes:read", "notes:write", "customers:read", "products:read"],
      "accessMode": "restricted",
      "storedGrants": ["company-reference:read", "company-reference:write", "shipping-options:read", "shipping-options:write", "search:read", "tags:read", "tags:write", "warehouses:read", "warehouses:write", "inventory:read", "lots:read", "reorder:read", "reorder:write", "notes:read", "notes:write", "customers:read", "products:read"],
      "effectiveScopes": ["company-reference:read", "company-reference:write", "shipping-options:read", "shipping-options:write", "search:read", "tags:read", "tags:write", "warehouses:read", "warehouses:write", "inventory:read", "lots:read", "reorder:read", "reorder:write", "notes:read", "notes:write", "customers:read", "products:read"],
      "retiredScopes": [],
      "expiresAt": null
    },
    "actor": { "type": "user", "id": "user-id", "displayName": "Jordan Buyer" },
    "documentation": {
      "openapiJsonUrl": "https://app.shelfcycle.com/api/v1/openapi.json",
      "llmsTxtUrl": "https://app.shelfcycle.com/llms.txt"
    },
    "effectiveCapabilities": {
      "documentUploads": { "create": false },
      "search": { "viewableTypes": ["customer", "contact", "product", "order", "purchase_order", "location"] },
      "entityResolution": { "viewableClasses": ["customer", "product", "product_family", "warehouse"] },
      "tags": { "vocabulary": true, "viewableEntityTypes": ["customer", "product", "product_family"] },
      "productDirectory": { "view": true, "createdRecordDiscovery": true },
      "notes": {
        "create": true,
        "view": true,
        "update": true,
        "activityReport": true,
        "viewableSubjectTypes": ["customer", "product"]
      },
      "opportunities": { "view": false, "createdRecordDiscovery": false, "create": false, "update": false, "close": false, "metaUpsert": false },
      "contacts": {
        "create": false,
        "view": false,
        "update": false,
        "archive": false,
        "unarchive": false,
        "customer": { "create": false, "view": false, "update": false, "archive": false, "unarchive": false },
        "supplier": { "create": false, "view": false, "update": false, "archive": false, "unarchive": false }
      },
      "customers": {
        "create": false,
        "view": true,
        "update": false,
        "archive": false,
        "unarchive": false,
        "activityFilter": false,
        "createdRecordDiscovery": true,
        "locations": { "view": true, "create": false, "update": false },
        "addresses": { "view": true, "create": false, "update": false, "archive": false, "unarchive": false },
        "documents": { "view": true, "create": false }
      },
      "suppliers": {
        "create": false,
        "view": false,
        "createdRecordDiscovery": false,
        "update": false,
        "archive": false,
        "unarchive": false,
        "shipFromLocations": { "view": false, "create": false, "update": false, "archive": false, "unarchive": false },
        "documents": { "view": false, "create": false }
      },
      "companyReference": {
        "activeDirectory": true,
        "customers": true,
        "suppliers": false,
        "representatives": true,
        "paymentTerms": true,
        "ledgerAccounts": true,
        "write": {
          "salesRepresentatives": false,
          "customerServiceRepresentatives": false,
          "supplierRepresentatives": false,
          "paymentTermsCreate": false,
          "paymentTermsMetadataUpdate": false,
          "ledgerAccountsCreate": true,
          "ledgerAccountsUpdate": true
        }
      },
      "shippingOptions": { "view": true, "create": true, "update": true, "archive": true, "unarchive": true },
      "products": {
        "view": true,
        "create": false,
        "update": false,
        "archive": false,
        "unarchive": false,
        "packaging": { "view": true, "create": false },
        "codeAttributes": { "view": true },
        "attributeAssignments": { "view": true, "update": false },
        "suppliers": { "view": false, "create": false, "remove": false },
        "aliases": { "view": true, "create": false, "update": false, "remove": false },
        "customerPartNumbers": { "view": true, "create": false, "update": false, "remove": false },
        "documents": { "view": true, "create": false },
        "sds": { "view": true, "update": false }
      },
      "productFamilies": {
        "view": true,
        "create": false,
        "update": false,
        "aliases": { "view": true, "create": false, "update": false, "remove": false }
      },
      "costBookEntries": { "view": false, "createdRecordDiscovery": false, "create": false, "delete": false },
      "currentCostSources": { "read": false, "write": false },
      "priceBookEntries": { "read": false, "createdRecordDiscovery": false, "write": false, "writeCurrencyMode": "org_base_only" },
      "orders": { "view": false, "createdRecordDiscovery": false, "changedRecordDiscovery": false },
      "purchaseOrders": {
        "view": false,
        "createdRecordDiscovery": false,
        "changedRecordDiscovery": false,
        "execution": { "view": false, "currentStateRefresh": false },
        "documents": { "view": false, "create": false }
      },
      "inventory": {
        "balances": { "view": false, "currentStateRefresh": false },
        "lots": { "view": true, "createdRecordDiscovery": true, "currentStateRefresh": true }
      },
      "reorder": { "status": { "view": false, "currentStateRefresh": false } },
      "warehouses": { "view": true, "create": true, "update": true, "archive": true, "unarchive": true },
      "reorderPoints": { "write": true },
      "certifications": { "view": true, "createdRecordDiscovery": true, "viewableSubjectTypes": ["customer", "product"] },
      "reports": { "available": [] },
      "writeReadiness": {
        "shippingOptions": { "create": true, "update": true, "archive": true, "unarchive": true },
        "warehouses": { "create": true, "update": true, "archive": true, "unarchive": true },
        "reorderPoints": { "write": true },
        "tags": { "create": true, "update": true, "assignableEntityTypes": ["customer", "product", "product_family"] },
        "notes": { "create": true, "update": true },
        "opportunities": { "create": false, "update": false, "close": false, "metaUpsert": false },
        "contacts": {
          "create": false,
          "update": false,
          "archive": false,
          "unarchive": false,
          "customer": { "create": false, "update": false, "archive": false, "unarchive": false },
          "supplier": { "create": false, "update": false, "archive": false, "unarchive": false }
        },
        "customers": {
          "create": false,
          "update": false,
          "archive": false,
          "unarchive": false,
          "locations": { "create": false, "update": false },
          "addresses": { "create": false, "update": false, "archive": false, "unarchive": false },
          "documents": { "create": false }
        },
        "suppliers": {
          "create": false,
          "update": false,
          "archive": false,
          "unarchive": false,
          "shipFromLocations": { "create": false, "update": false, "archive": false, "unarchive": false },
          "documents": { "create": false }
        },
        "products": {
          "create": false,
          "update": false,
          "archive": false,
          "unarchive": false,
          "packaging": { "create": false },
          "suppliers": { "create": false, "remove": false },
          "aliases": { "create": false, "update": false, "remove": false },
          "customerPartNumbers": { "create": false, "update": false, "remove": false },
          "documents": { "create": false },
          "sds": { "update": false }
        },
        "productFamilies": {
          "create": false,
          "update": false,
          "aliases": { "create": false, "update": false, "remove": false }
        },
        "costBookEntries": { "create": false, "delete": false },
        "currentCostSources": { "write": false },
        "priceBookEntries": { "create": false },
        "purchaseOrders": { "documents": { "create": false } }
      }
    }
  }
}
```

> **Information**: Use the key preview, org name, contract version, and effective capabilities as the wiring check. Never print the full key.

## 3. Cache active company hints when needed

For inbox triage, intake review, or duplicate checks, use the active reference directory to page safe customer and supplier matching hints into a local cache.

```bash
curl "$SHELFCYCLE_API_BASE_URL/reference/customers?limit=250" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "X-ShelfCycle-Client: Email notes automation"
```

Reference rows are active-only hints. They are not complete profiles, not archived exports, and not write authorization. If `meta.hasMore` is true, continue with `meta.nextCursor`.

For commercial account setup, resolve representatives and payment terms through `GET /representatives` and `GET /payment-terms`. Resolve an eligible supplier cost account through `GET /reference/ledger-accounts`. The [Account setup guide](/guides/account-setup) gives the complete reference-first customer and supplier sequence.

## 4. Resolve the target record

Use `GET /entity-resolution` when the workflow knows the record class and needs a stable ID. Send the source wording in `q` and choose `customer`, `supplier`, `product`, `product_family`, `sales_order`, `purchase_order`, or `salesperson` as `class`.

```bash
curl "$SHELFCYCLE_API_BASE_URL/entity-resolution?q=Northstar%20Chemical&class=customer" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "X-ShelfCycle-Client: Email notes automation"
```

```json
{
  "data": {
    "classesSearched": ["customer"],
    "classResults": [{ "matchedClass": "customer", "totalMatches": 1 }],
    "candidates": [
      {
        "entityId": "customer-id",
        "displayName": "Northstar Chemical",
        "matchedClass": "customer",
        "matchRank": "exact",
        "matchBasis": "canonical",
        "matchedValue": "Northstar Chemical"
      }
    ],
    "archivedRecordsExcluded": true,
    "selectionStatus": "auto_selected",
    "selectedCandidate": { "entityId": "customer-id", "matchedClass": "customer" }
  },
  "meta": {
    "capabilityKey": "resolve_entity",
    "capabilityVersion": "1.1.0"
  }
}
```

Use `selectedCandidate.entityId` when `selectionStatus` is `auto_selected`. If the response is `needs_clarification`, use the candidate names and match evidence to narrow the choice. Keep a `no_match` result unresolved rather than guessing. See [Entity Resolution](/guides/entity-resolution) for all classes and response fields.

Existing connections can continue using `GET /search` for broader discovery across several resource types, including contacts and locations. For new record binding, use Entity Resolution, Tags, Product Directory, or a typed finder for the job at hand.

If a request needs repair, validation errors can include a machine-readable next step. Use fields such as `repairCategory`, `suggestedAction`, `validValues`, `acceptedFilters`, `requiredCombination`, `repairContext`, `documentationPath`, and `example` to change the request once before retrying.

```json
{
  "error": {
    "type": "validation_error",
    "code": "validation_failed",
    "message": "Request validation failed.",
    "param": "class",
    "suggestedAction": "choose_valid_value",
    "repairCategory": "invalid_enum",
    "validValues": { "class": ["customer", "supplier", "product", "product_family", "sales_order", "purchase_order", "salesperson"] },
    "documentationPath": "/api/v1/openapi.json",
    "doNotRetrySameRequest": true,
    "requestId": "request-id"
  }
}
```

## 5. Hydrate exact context

After selecting a resource, fetch the exact detail, child list, attached document list, or order document endpoint when the workflow needs more context.

```bash
curl "$SHELFCYCLE_API_BASE_URL/customers/customer-id" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"
```

For typed lookup before detail, use finder endpoints such as `GET /customers?q=Northstar`, `GET /products?code=ACETONE-PAIL`, `GET /orders?orderNo=SO-10042`, or `GET /purchase-orders?orderNo=PO-4812`. Company and product finders require a supported narrowing predicate. Order finders accept exact identifiers, status, customer or supplier, products, related orders, business dates, and last-updated ranges. Exact order-number, customer-order-number, and external-id filters accept as many as 25 comma-separated values where documented.

For recently changed sales and purchase orders, send paired `changedFrom` and `changedTo` timestamps. Continue larger finder results with `meta.nextCursor`, then fetch exact documents by id. Order detail returns 100 lines by default, accepts as many as 500, and continues with `lineCoverage.nextLineCursor` by itself.

For open purchasing workflows, use `GET /purchase-order-execution` to review current purchase lines, expected and received quantities, and receipt progress. Filter by `warehouseId` to line purchasing up with warehouse-level inventory and reorder views. Use `executionChangedAt` to identify newer operational activity, follow `receiptEventsUrl` for exact receipt history, and follow `documentsUrl` for purchase-order files. Periodically restart the original query to refresh the complete current view. See the [Purchase execution guide](/guides/purchase-execution) for filters, continuation, and document examples.

After selecting a customer or supplier, use parent-scoped child endpoints when the workflow needs exact locations or addresses: `GET /customers/{id}/locations`, `GET /customers/{id}/locations/{locationId}/addresses`, or `GET /suppliers/{id}/ship-from-locations`.

When a selected customer, supplier, contact, customer address, or supplier ship-from location should be retired or restored, preview its named archive or unarchive action and apply it with the latest `updatedAt` value. See [Archive and restore](/guides/archive-restore) for the endpoint map and receipt flow.

For product catalog setup, resolve families and packaging through `GET /product-families` and `GET /reference/product-packaging`, create packaging through `POST /product-packaging` when needed, and read code definitions through `GET /product-code-attributes` plus the selected attribute's `/values` endpoint. Product create and update requests can save typed `attributeAssignments` with the product. Afterward, connect suppliers, organization-wide or relationship-specific aliases, and customer part numbers through the product's catalog links. See [Product setup](/guides/product-setup) for the complete sequence.

When the workflow starts from an organization tag, use `GET /tags` to browse the vocabulary and `GET /tagged-records` to find authorized records carrying one exact tag. With `tags:write`, connected workflows can create or update shared tags and use `POST` or `DELETE /tag-assignments` to maintain membership across customers, suppliers, products, contacts, opportunities, orders, and addresses. See the [Tags guide](/guides/tags) for the complete sequence.

When the workflow starts from a UN/NA number, CAS number, hazard class, packing group, or product name, use `GET /product-directory` to find the matching product and family population. See the [Product Directory guide](/guides/product-directory) before selecting a detail endpoint.

For customer, supplier, product, or purchase-order source files, use `POST /document-uploads`, send the file with the returned upload values, then attach `data.document` through the exact parent route. A successful uploaded-file attachment returns a durable `data.integrity` receipt, and an exact retry returns the same stored receipt. Product workflows can assign an uploaded PDF or image as the current SDS with `PUT /products/{id}/sds`. See the [Attached documents guide](/guides/attached-documents) for the complete flow.

For opportunity workflows, use `GET /opportunities` to find visible customer- and supplier-side opportunities by status, owner, product, relationship, created date, or matching text. Fetch `GET /opportunities/{id}` before an update, then use the latest `updatedAt` value for `If-Match`. See the [Opportunities guide](/guides/opportunities) for create, update, close, and metadata examples.

For report workflows, call `GET /reports` before running a report. Use `allowedForThisKey` and `effectiveCapabilities.reports.available` to choose report keys the current key and actor can run. Customer AR Intelligence adds final-settlement behavior, current credit exposure, and matched invoice charges to the existing receivables aging view. The [Reports guide](/guides/reports) covers these views alongside sales, purchasing, open-order, and pipeline reporting.

For organization-wide note review, use `GET /note-activity` to count notes by day, type, author, or subject, or to browse the newest matching excerpts. See the [Note activity guide](/guides/note-activity) for date windows, filters, and continuation.

For product cost workflows, use `GET /cost-book-entries` to read current Cost Book entries or exact product, supplier, and ship-from history. Read `GET /products/{id}/current-cost-source` when the workflow needs the product's fixed or selected supplier origin. Use `GET /products/{id}/cost-context` to follow that source, or include `supplierId` for one explicit supplier. `POST /cost-book-entries` creates the next cost version, `PATCH /cost-book-entries/{id}` corrects a selected event while preserving history, `DELETE /cost-book-entries/{id}` removes one mistaken direct, Landed, or Built from entry when its preview is ready, and `POST /cost-book-entries/batch` applies up to 50 approved updates or corrections. See the [Cost Book guide](/guides/cost-book) before maintaining Cost Book entries or Current Source.

For current physical stock, use `GET /inventory-balances` to review on-hand, allocated, available, and quarantined-on-hand quantities by product and warehouse. Keep each row's quantity basis with its values, and see the [Inventory balances guide](/guides/inventory-balances) for filtering and refresh guidance.

For lot-level stock, use `GET /lots` to find named lots with exact on-order, on-hand, allocated, and available quantities, COA readiness, and remaining-stock age. After choosing one lot, use `GET /lots/{id}` for the same COA readiness and the inventory additions behind that age. See the [Lots guide](/guides/lots) for filters, quantity basis, COA readiness, age evidence, and continuation.

For stock-out questions, use the inventory response's complete census instead of counting the current page. For configured thresholds, use `GET /reorder-status` to review current stock, allocated and committed demand, projected availability, projected deficit, and status. See the [Reorder status guide](/guides/reorder-status).

For certification questions, use `GET /certification-types` to learn the organization's vocabulary, `GET /certification-records` for stored records, and `GET /certification-subjects` for the authorized customers, suppliers, products, or customer addresses with matching records. See [Certification Intelligence](/guides/certifications).

For customer and list pricing workflows, use `GET /price-book-entries` to review active, scheduled, expired, or historical configured prices with exact package and base-unit context. See the [Price Book guide](/guides/price-book) before previewing and applying a single-price update.

## 6. Preflight the note write

For supported POST, PATCH, PUT, and DELETE writes, add `dryRun=true` to the same endpoint before execution when your workflow wants a readiness check. The dry run does not write. It uses the same scope, live permission, and rate-limit family as execution.

```bash
curl "$SHELFCYCLE_API_BASE_URL/notes?dryRun=true" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: gmail-thread-123:customer-id:create-note" \
  -d '{
    "primarySubject": { "type": "customer", "id": "customer-id" },
    "linkedRecords": [
      { "type": "contact", "id": "contact-id" },
      { "type": "product", "id": "product-id" }
    ],
    "noteType": "EMAIL",
    "title": "Inbound product thread",
    "body": "Jordan asked for follow-up on acetone availability.",
    "happenedAt": "2026-05-28T16:00:00.000Z"
  }'
```

```json
{
  "data": {
    "type": "write_readiness",
    "operation": "notes.create",
    "status": "ready",
    "wouldWrite": false,
    "checks": [
      { "code": "scope_authorized", "status": "passed", "message": "The key can create notes." },
      { "code": "target_available", "status": "passed", "message": "The subject is visible." },
      { "code": "idempotency_available", "status": "passed", "message": "The idempotency key is available." }
    ],
    "duplicateCandidates": [],
    "submit": { "method": "POST", "path": "/api/v1/notes", "query": {} },
    "verification": {
      "available": true,
      "method": "GET",
      "path": null,
      "pathTemplate": "/api/v1/notes/{id}",
      "requiresScopes": ["notes:read"],
      "fallback": "detail_get"
    },
    "requestId": "request-id"
  }
}
```

`status: "blocked"` can still return HTTP 200. Inspect `checks` and `duplicateCandidates` before deciding whether your policy allows execution.

## 7. Execute the note write

Remove `dryRun=true` when your workflow's policy allows execution. Send a stable `Idempotency-Key` built from source facts, not a random value.

```bash
curl "$SHELFCYCLE_API_BASE_URL/notes" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: gmail-thread-123:customer-id:create-note" \
  -d '{
    "primarySubject": { "type": "customer", "id": "customer-id" },
    "linkedRecords": [
      { "type": "contact", "id": "contact-id" },
      { "type": "product", "id": "product-id" }
    ],
    "noteType": "EMAIL",
    "title": "Inbound product thread",
    "body": "Jordan asked for follow-up on acetone availability.",
    "happenedAt": "2026-05-28T16:00:00.000Z"
  }'
```

```json
{
  "data": {
    "id": "note-id",
    "type": "note",
    "url": "https://app.shelfcycle.com/org-northstar/notes/note-id",
    "createdAt": "2026-05-29T14:35:00.000Z",
    "updatedAt": "2026-05-29T14:35:00.000Z",
    "createdBy": { "type": "user", "id": "user-id", "displayName": "Jordan Buyer" },
    "idempotencyStatus": "created"
  }
}
```

Use the returned `url` or the readiness response's verification guidance for readback. If detail verification is not available for the key, treat the execute response as the readback.

> **Warning**: Do not generate a random idempotency key for each retry. The same source event must reuse the same key. The same key with a different body returns `409 idempotency_key_reused`.

## 8. Build with the v1.44.1 capabilities

v1.44.1 expands Cost Book maintenance across every Arrangement kind, including added costs, exact Built from lanes, and previewed removal with restoration. It builds on Supplier History, batched Product Directory name matching, invoice, bill, and payment reads, receivables and payables reporting, certification renewal, lot-level COA readiness, Created By context, Chart of Accounts and Shipping Options management, finder continuation, warehouse purchasing, warehouse and reorder-point setup, product and tag management, commercial account setup, inventory, orders, pricing, documents, notes, reports, and readiness-aware writes.

Choose the narrowest endpoint for the job, verify effective capabilities through `GET /me`, and use the live OpenAPI reference for exact request and response shapes. See [Lots](/guides/lots) for lot quantities, COA readiness, and stock age; [Created records](/guides/created-records) for creator and created-time workflows; [Chart of Accounts](/guides/chart-of-accounts) for account setup and maintenance; [Shipping Options](/guides/shipping-options) for vocabulary maintenance; and [Finders](/guides/finders) for continuation rules.
