Contacts
Create and update approved contacts under existing customers or suppliers.
Create a contact
POST /contacts
Required scope:
contacts:create
Required header:
Idempotency-Key: <stable-key>
Create a contact under an existing customer or supplier. ShelfCycle rejects archived parents, cross-org ids, and exact duplicates under the same parent.
curl "$SHELFCYCLE_API_BASE_URL/contacts" \
-X POST \
-H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: gmail-thread-123:customer-id:create-contact" \
-d '{
"parent": { "type": "customer", "id": "customer-id" },
"name": "Jane Buyer",
"title": "Purchasing",
"email": "jane@example.com",
"phone": "555-0100",
"mobilePhone": "555-0101",
"faxNumber": "555-0102",
"isEmergency": true,
"documentTypes": ["PRICE_QUOTE", "INVOICE"]
}'
{
"data": {
"type": "contact",
"id": "contact-id",
"contactKind": "customer",
"displayName": "Jane Buyer",
"subtitle": "jane@example.com | Purchasing | 555-0100",
"url": "https://app.shelfcycle.com/org-northstar/customers/customer-id/contacts",
"parent": { "type": "customer", "id": "customer-id", "displayName": "Northstar Chemical" },
"createdAt": "2026-05-29T14:30:00.000Z",
"updatedAt": "2026-05-29T14:30:00.000Z",
"name": "Jane Buyer",
"title": "Purchasing",
"email": "jane@example.com",
"phone": "555-0100",
"mobilePhone": "555-0101",
"faxNumber": "555-0102",
"isEmergency": true,
"documentTypes": ["PRICE_QUOTE", "INVOICE"],
"idempotencyStatus": "created"
}
}
Repeating the same route plus Idempotency-Key with the same request body in the same org returns the existing contact with idempotencyStatus: "replayed", even after key rotation.
Read a contact
GET /contacts/{id}
Required scope:
search:read
The response includes the latest updatedAt value to use as If-Match.
Update a contact
PATCH /contacts/{id}
Required scope:
contacts:update
Required header:
If-Match: <updatedAt from latest response or GET /contacts/{id}>
Sparse patch fields:
{
"name": "Jane Buyer",
"title": "Director of Purchasing",
"email": "jane@example.com",
"phone": "555-0101",
"mobilePhone": "555-0102",
"faxNumber": null,
"isEmergency": false,
"documentTypes": ["SALES_CONFIRMATION"]
}
Omitted fields stay unchanged. title, email, phone, mobilePhone, and faxNumber can be sent as null to clear. name cannot be cleared.
Supplier contacts support title, email, phone, isEmergency, and supplier document types such as PURCHASE_ORDERS and LOGISTICS. They reject mobilePhone and faxNumber because the supplier-contact model has no matching fields.
If the record changed after the updatedAt value sent in If-Match, ShelfCycle returns 409 stale_record.
Parent reassignment, archive state, and hidden fields are not supported through v1.
