Notes
Create and update approved notes on confirmed customers, suppliers, and products.
Create a note
POST /notes
Required scope:
notes:write
Required header:
Idempotency-Key: <stable-key>
primarySubject can be customer, supplier, or product.
linkedRecords can be customer, supplier, product, contact, or location. ShelfCycle renders those records as mention and backlink rows and dedupes them with the primary subject.
Supported noteType values:
NOTE, EMAIL, PHONE_MEETING, VIRTUAL_MEETING, PHYSICAL_MEETING, SAMPLE_REQUEST, OPPORTUNITY, QUOTE
Request
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" },
{ "type": "supplier", "id": "supplier-id" }
],
"noteType": "EMAIL",
"title": "Inbound product approval thread",
"body": "Jordan approved adding the new contact and asked for follow-up on acetone availability.",
"happenedAt": "2026-05-28T16:00:00.000Z"
}'
Response
{
"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" },
"createdVia": { "type": "public_api", "actorUserId": "user-id" },
"requestedVia": { "type": "api_key", "id": "api-key-id", "purpose": "Workflow for creating notes from approved emails" },
"idempotencyStatus": "created"
}
}
Repeating the same route plus Idempotency-Key with the same request body in the same org returns the existing note with idempotencyStatus: "replayed" and does not mutate the existing note.
Read a note
GET /notes/{id}
Required scope:
notes:write
The response includes the latest updatedAt value to use as If-Match for updates.
Update a note
PATCH /notes/{id}
Required header:
If-Match: <updatedAt from latest response or GET /notes/{id}>
Sparse patch fields:
{
"title": "Updated title",
"body": "Updated approved note body.",
"happenedAt": "2026-05-28T18:00:00.000Z",
"noteType": "PHONE_MEETING",
"linkedRecords": [
{ "type": "customer", "id": "customer-id" },
{ "type": "contact", "id": "contact-id" }
]
}
PATCH only updates notes created through the public API for the same org. It cannot change the primary subject, creator, org, idempotency identity, archive state, or deletion state.
Orders are not valid note subjects or linked records in v1. Use order search results only as human-readable context in the note body.
