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.
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.
SHELFCYCLE_API_KEY=sk_...
SHELFCYCLE_API_BASE_URL=https://app.shelfcycle.com/api/v1
For local ShelfCycle development, use:
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.
curl "$SHELFCYCLE_API_BASE_URL/me" \
-H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
-H "X-ShelfCycle-Client: Email notes automation"
The response should include the current API v1.49.2 contract, 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 for the complete compatibility check.
{
"data": {
"apiVersion": "v1",
"apiContractVersion": "v1.49.2",
"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, "coa": { "view": true, "update": false } }
},
"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"], "records": { "create": false, "update": false, "writableSubjectTypes": [] } },
"reports": { "available": [] },
"writeReadiness": {
"certifications": { "records": { "create": false, "update": false, "writableSubjectTypes": [] } },
"lots": { "coa": { "update": false } },
"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 } }
}
}
}
}
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.
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 gives the complete reference-first customer and supplier sequence.
For supplier carrier workflows, use GET /suppliers?carrier=true to find carriers, then read or maintain /suppliers/{id}/carrier-profile. Carrier changes use the profile's own updatedAt for If-Match. See Supplier carriers for complete examples.
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.
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"
{
"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 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.
{
"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.
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 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 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 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 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 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 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 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 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 for date windows, filters, and continuation.
For communication workflows, use GET /email-activity to review sent emails alongside email notes, group activity, or prepare customer follow-up. Open a selected sent message through GET /sent-emails/{id}. See Sent email activity for access and examples.
For notes that need a teammate's attention, include { "type": "user", "id": "teammate-user-id" } in linkedRecords. ShelfCycle shows an inline mention and sends the normal in-app notification when eligible. See Notes before maintaining the complete link and mention list.
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 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 for filtering and refresh guidance.
For lot-level stock, use GET /lots to find named lots with exact quantities, product code and family, COA readiness, and remaining-stock age. Send paired addedFrom and addedTo calendar days to review inventory added during a period, including lots that have since been depleted. Add includeDirectShip=true when needed. After choosing one lot, use GET /lots/{id} for the remaining inventory additions behind its stock age. See the Lots guide for date windows, purchase-order access, quantity basis, COA maintenance, and continuation.
For supplier bill review, GET /bills/{id} includes cost descriptions and linked orders in costs, plus amounts and ledger accounts in items. These are two views of the same money, not amounts to add together. Use the bill's header total and check lineCoverage when presenting its detail. See Invoices, bills, and payments.
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.
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.
For certificate maintenance, upload the supporting file, then use POST /certification-records to file it or PATCH /certification-records/{recordId} to renew or complete a selected record. Certificate writes use calendar-day dates and certifications:write. For lot COAs, read GET /lots/{id}/coa and send its returned version as If-Match when replacing the file with PUT /lots/{id}/coa. See Certification Intelligence and Lots for complete examples.
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 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.
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"
}'
{
"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.
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"
}'
{
"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.
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.49 capabilities
v1.49 adds certificate filing, renewal, pending-record completion, and lot COA maintenance with supporting uploads and previews. It builds on supplier carrier profiles, teammate mentions, sent email activity, complete Cost Book maintenance, Supplier History, batched Product Directory name matching, invoices, bills, payments, receivables and payables reporting, certifications, lots, Chart of Accounts, Shipping Options, inventory, purchasing, product and tag management, commercial accounts, pricing, documents, and supported 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 for lot quantities, COA readiness, and stock age; Created records for creator and created-time workflows; Chart of Accounts for account setup and maintenance; Shipping Options for vocabulary maintenance; and Finders for continuation rules.