Notes

List, read, create, and update notes on confirmed customers, suppliers, and products, with related records and teammate mentions.

Use this guide after selecting one customer, supplier, or product. For complete counts, rankings, and recent excerpts across notes, use the Note activity guide.

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"

Add happenedAtFrom and happenedAtTo to narrow the selected subject's notes by organization-calendar day. Add noteType to narrow the list to one supported type.

curl "$SHELFCYCLE_API_BASE_URL/notes?primarySubjectType=customer&primarySubjectId=customer-id&happenedAtFrom=2026-07-13&happenedAtTo=2026-07-19&noteType=EMAIL&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.

Use externalId for the source note's stable identity. Keep it separate from the Idempotency-Key used for safe retries.

primarySubject can be customer, supplier, or product.

linkedRecords can be customer, supplier, product, contact, location, or user. ShelfCycle connects the related records to the note and shows linked users as inline @mentions. Use each teammate's user ID from your organization.

Mentioned teammates receive the normal in-app notification when they can read the note and have in-app notifications enabled. The author does not receive a notification for their own mention, and an update does not notify someone already mentioned again.

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" },
      { "type": "user", "id": "teammate-user-id" }
    ],
    "noteType": "EMAIL",
    "title": "Inbound product thread",
    "body": "Jordan asked for follow-up on acetone availability.",
    "happenedAt": "2026-05-28T16:00:00.000Z",
    "externalId": "GMAIL-MESSAGE-7781"
  }'

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",
    "externalId": "GMAIL-MESSAGE-7781",
    "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.

User links are returned in linkedRecords as { "type": "user", "id": "teammate-user-id" }, so a workflow can retain the same mentions in a later update.

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",
  "externalId": "GMAIL-MESSAGE-7781",
  "linkedRecords": [
    { "type": "customer", "id": "customer-id" },
    { "type": "contact", "id": "contact-id" },
    { "type": "user", "id": "teammate-user-id" }
  ]
}

PATCH only updates notes created through the public API for the same organization. It can maintain externalId but cannot change the primary subject, creator, idempotency identity, archive state, or deletion state.

When included, linkedRecords replaces the complete list. Include every related record and teammate to retain; omit the field to keep the current list, or send [] to clear it. Leaving out a user removes their mention.

A user ID never linked to your organization returns 404 without saving the note. A former organization user remains visible in existing note links; remove that user from the replacement list before updating the links. Keeping or adding them returns 422 archived_record.

Warning

Use a customer, supplier, or product as the primary subject. Add related records and teammates through linkedRecords, and include order context in the note body.