Skip to main content
Git notes attach text metadata to Git objects without changing the object or the commit history around it. They are useful for build status, review state, agent trace data, agent rewards for RL training, handoff notes, and other context that should travel with a commit but should not be committed into the repository tree. Code Storage supports the default Git notes ref and custom notes refs. The default is refs/notes/commits, which matches Git’s own default for git notes.

Choose a notes ref

Use the default ref when all notes belong to one stream. Use a custom notes ref when different systems need independent note namespaces. For example:
  • ci for build and deploy status
  • reviews for review summaries
  • agents/session-123 for per-agent handoff state
Pass ref to the SDK note methods to target a custom notes ref:
const ref = "reviews";

await repo.createNote({
  sha: "abc123def456...",
  note: "Reviewed. Ready for production.",
  ref,
});

const note = await repo.getNote({ sha: "abc123def456...", ref });
ref = "reviews"

await repo.create_note(
    sha="abc123def456...",
    note="Reviewed. Ready for production.",
    ref=ref,
)

note = await repo.get_note(sha="abc123def456...", ref=ref)
ref := "reviews"

_, err := repo.CreateNote(context.Background(), storage.CreateNoteOptions{
	SHA:  "abc123def456",
	Note: "Reviewed. Ready for production.",
	Ref:  ref,
})

note, err := repo.GetNote(context.Background(), storage.GetNoteOptions{
	SHA: "abc123def456",
	Ref: ref,
})
The REST API uses the same name: pass ref in the write/delete request body, or as a query parameter when reading a note.
curl "$CODE_STORAGE_BASE_URL/repos/my-repo/notes" \
  -H "Authorization: Bearer $CODE_STORAGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sha": "abc123def456",
    "action": "add",
    "note": "Reviewed. Ready for production.",
    "ref": "reviews"
  }'
curl "$CODE_STORAGE_BASE_URL/repos/my-repo/notes?sha=abc123def456&ref=reviews" \
  -H "Authorization: Bearer $CODE_STORAGE_TOKEN"

Ref rules

Empty or omitted ref values use refs/notes/commits. Custom notes refs are canonicalized like Git’s git notes --ref option:
InputCanonical ref
reviewsrefs/notes/reviews
notes/reviewsrefs/notes/reviews
refs/notes/reviewsrefs/notes/reviews
If the input is already fully qualified with a refs/ prefix, it must be a safe refs/notes/* ref. Short inputs are resolved inside refs/notes/ before the same validation runs. The server rejects refs outside the notes namespace and unsafe ref paths, including parent traversal, empty path segments, trailing slashes, trailing dots, and .lock suffixes. Invalid notes refs return 400.

List notes refs

Use the notes refs list API to discover custom notes namespaces before reading individual notes. It returns notes refs under a refs/notes/ prefix in lexical order.
curl "$CODE_STORAGE_BASE_URL/repos/my-repo/notes/refs?prefix=reviews/&limit=20" \
  -H "Authorization: Bearer $CODE_STORAGE_TOKEN"
{
  "refs": [
    {
      "cursor": "opaque-cursor",
      "ref": "refs/notes/reviews/session-a",
      "sha": "a1b2c3d4e5f6..."
    }
  ],
  "next_cursor": "opaque-cursor",
  "has_more": true,
  "prefix": "refs/notes/reviews/"
}
The prefix is canonicalized like the note ref above: short inputs resolve inside refs/notes/, and a fully qualified refs/notes/* prefix also works. A trailing slash restricts the listing to descendants of that ref. Omit prefix to list everything under refs/notes/.
PrefixCanonical prefixMatches
(omitted)refs/notes/All notes refs
reviewsrefs/notes/reviewsrefs/notes/reviews and descendants
reviews/refs/notes/reviews/Descendants under refs/notes/reviews/
refs/notes/reviewsrefs/notes/reviewsrefs/notes/reviews and descendants
Use next_cursor as the cursor query parameter to read the next page. limit defaults to 20 and is capped at 100. Responses echo the normalized full prefix and full notes ref names. Invalid prefixes and cursors return 400.

Write behavior

createNote() creates a new note and fails if the target already has a note on the selected notes ref. To replace a note, delete it first with deleteNote(), then create the new note. appendNote() appends text to the note on the selected ref. If no note exists, the server creates one. deleteNote() removes the note from the selected ref. targetRef in write responses is always the canonical full ref, such as refs/notes/reviews. refSha in read responses is the current SHA of the notes ref that was read.

Concurrency and policies

expectedRefSha protects the selected notes ref with optimistic concurrency. When ref is omitted, the guard applies to refs/notes/commits. When ref is set, it applies to that custom notes ref. refPolicies are evaluated against the selected notes ref for createNote(), appendNote(), and deleteNote(). See the Branch Protection guide.