Notes
List, read, create, and update supported CRM notes on confirmed customers, suppliers, and products.
List subject notes
GET /notes
Required scope:
notes:read
GET /notes requires a visible subject and returns metadata only. It is not a global note search.
curl "$SHELFCYCLE_API_BASE_URL/notes?primarySubjectType=customer&primarySubjectId=customer-id&limit=10" \
-H "Authorization: Bearer $SHELFCYCLE_API_KEY"
Fetch the body only after selecting one visible note with GET /notes/{id}.
Create a note
POST /notes
Required scope:
notes:write
Required header:
Idempotency-Key: <stable-key>
Add dryRun=true to check the exact intended note write without mutating records.
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 thread",
"body": "Jordan 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 source 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:read
For v1 compatibility, notes:write can read API-created notes. The response includes the latest updatedAt value to use as If-Match for updates.
Update a note
PATCH /notes/{id}
Required scope:
notes:write
Required header:
If-Match: <updatedAt from latest GET /notes/{id}>
Sparse patch fields:
{
"title": "Updated title",
"body": "Updated 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 and purchase orders are not valid note subjects or linked records in v1.20. Use order document context as plain text in the note body.