Skip to main content

Drafts & Branches

The Drafts router provides a merge-before-persist workflow for memory changes. Instead of writing directly to canonical memory, agents create drafts that can be reviewed, diffed against existing memories, and then merged or rejected. Drafts can be grouped into branches for batch review.

Base URL: https://novyx-ram-api.fly.dev

Tier: All tiers. Plan limits apply to open drafts and active branches.

Plan limits: Vary by tier (open draft count and active branch count are capped per plan).


Create Draft

POST /v1/memory-drafts

Create a reviewable memory draft without mutating canonical memory. The draft is compared against existing memories to find similar entries and generate a review summary.

Request body

ParameterTypeRequiredDefaultDescription
observationstringYesMemory content
contextstringNoAdditional context
context_idsstring[]No[]Linked context IDs
agent_idstringNoAgent identifier
tagsstring[]No[]Tags for categorization
importancenumberNo5Importance score (1-10)
confidencenumberNo1.0Confidence score (0-1)
space_idstringNoContext space to store into
branch_idstringNoBranch/session identifier for grouping drafts
ttl_secondsnumberNoTime-to-live in seconds (sets expiry on merge)

Response fields

FieldTypeDescription
draft_idstringDraft identifier
branch_idstring | nullBranch this draft belongs to
statusstringdraft, merged, or rejected
created_atstringISO 8601 timestamp
updated_atstringISO 8601 timestamp
observationstringMemory content
contextstring | nullAdditional context
agent_idstring | nullAgent identifier
tagsstring[]Tags
importancenumberImportance score
confidencenumberConfidence score
space_idstring | nullContext space
review_summaryobjectReview summary with proposed changes, similar memory count, and matches
merged_memory_idstring | nullID of created memory (after merge)
merged_atstring | nullMerge timestamp
rejected_atstring | nullRejection timestamp
rejection_reasonstring | nullReason for rejection

Examples

from novyx import Novyx

nx = Novyx(api_key="nram_your_key")

draft = nx.draft_memory(
observation="User prefers dark mode with compact layouts",
tags=["preferences", "ui"],
importance=7,
branch_id="session-2026-03-15"
)
print(f"Draft ID: {draft['draft_id']}")
print(f"Similar memories: {draft['review_summary']['similar_count']}")

Response

{
"draft_id": "draft_a1b2c3d4",
"branch_id": "session-2026-03-15",
"status": "draft",
"created_at": "2026-03-15T14:00:00Z",
"updated_at": "2026-03-15T14:00:00Z",
"observation": "User prefers dark mode with compact layouts",
"context": null,
"agent_id": null,
"tags": ["preferences", "ui"],
"importance": 7,
"confidence": 1.0,
"space_id": null,
"review_summary": {
"proposed_changes": ["observation", "tags", "importance"],
"similar_count": 1,
"similar_memories": [
{
"uuid": "mem_xyz789",
"observation": "User likes dark mode",
"similarity": 0.87
}
]
},
"merged_memory_id": null,
"merged_at": null,
"rejected_at": null,
"rejection_reason": null
}

Errors

StatusCodeCause
403draft_limit_reachedOpen draft limit exceeded for your plan
403branch_limit_reachedActive branch limit exceeded for your plan

List Drafts

GET /v1/memory-drafts

List memory drafts for the current tenant, optionally filtered by status or branch.

Query parameters

ParameterTypeRequiredDefaultDescription
statusstringNoFilter by status: draft, merged, or rejected
branch_idstringNoFilter by branch/session identifier

Response fields

FieldTypeDescription
total_countnumberTotal drafts matching the filter
draftsarrayArray of draft objects (same structure as Create Draft response)

Examples

drafts = nx.memory_drafts(status="draft")
print(f"Open drafts: {drafts['total_count']}")
for d in drafts["drafts"]:
print(f" {d['draft_id']}: {d['observation'][:50]}...")

Get Draft

GET /v1/memory-drafts/{draft_id}

Fetch a single memory draft by ID.

Path parameters

ParameterTypeDescription
draft_idstringDraft identifier

Examples

draft = nx.get_draft("draft_a1b2c3d4")
print(f"Status: {draft['status']}")
print(f"Observation: {draft['observation']}")

Errors

StatusCodeCause
404drafts.not_foundDraft does not exist

Draft Diff

GET /v1/memory-drafts/{draft_id}/diff

Show a field-level diff between a draft and an existing memory. If no comparison target is specified, the closest matching memory is used automatically.

Path parameters

ParameterTypeDescription
draft_idstringDraft identifier

Query parameters

ParameterTypeRequiredDefaultDescription
compare_tostringNoExisting memory UUID to compare against

Response fields

FieldTypeDescription
draft_idstringDraft identifier
compared_memory_idstring | nullMemory being compared against
comparison_basisstringexplicit, closest_match, or none
recommendationstringmerge_new, merge_or_supersede, merge_update, or check_duplicate
changed_fieldsarrayField-level diff for each comparable field
current_memoryobject | nullThe existing memory (if comparing)
proposed_memoryobjectThe draft as a memory object

Each changed field includes:

FieldTypeDescription
fieldstringField name
currentanyCurrent value in existing memory
proposedanyProposed value in draft
changedbooleanWhether the field differs

Examples

diff = nx.draft_diff("draft_a1b2c3d4")
print(f"Recommendation: {diff['recommendation']}")
for field in diff["changed_fields"]:
if field["changed"]:
print(f" {field['field']}: {field['current']} -> {field['proposed']}")

Response

{
"draft_id": "draft_a1b2c3d4",
"compared_memory_id": "mem_xyz789",
"comparison_basis": "closest_match",
"recommendation": "merge_or_supersede",
"changed_fields": [
{"field": "observation", "current": "User likes dark mode", "proposed": "User prefers dark mode with compact layouts", "changed": true},
{"field": "importance", "current": 5, "proposed": 7, "changed": true},
{"field": "tags", "current": ["preferences"], "proposed": ["preferences", "ui"], "changed": true}
],
"current_memory": {
"uuid": "mem_xyz789",
"observation": "User likes dark mode"
},
"proposed_memory": {
"uuid": "draft_a1b2c3d4",
"observation": "User prefers dark mode with compact layouts"
}
}

Errors

StatusCodeCause
404drafts.not_foundDraft does not exist
404memories.not_foundComparison memory not found

Merge Draft

POST /v1/memory-drafts/{draft_id}/merge

Merge a reviewed draft into canonical memory. Optionally supersede an existing memory in the same operation.

Path parameters

ParameterTypeDescription
draft_idstringDraft identifier

Request body

ParameterTypeRequiredDefaultDescription
supersede_memory_idstringNoExisting memory to supersede on merge

Response fields

FieldTypeDescription
draft_idstringDraft identifier
statusstringmerged
memory_idstringID of the created canonical memory
hashstringIntegrity hash of the new memory
created_atstringISO 8601 timestamp
messagestringConfirmation message
superseded_memory_idstring | nullID of the superseded memory (if applicable)

Examples

result = nx.merge_draft("draft_a1b2c3d4")
print(f"Merged! Memory ID: {result['memory_id']}")

# Merge and supersede an existing memory
result = nx.merge_draft(
"draft_a1b2c3d4",
supersede_memory_id="mem_xyz789"
)
print(f"Superseded: {result['superseded_memory_id']}")

Response

{
"draft_id": "draft_a1b2c3d4",
"status": "merged",
"memory_id": "mem_new456",
"hash": "sha256:abc123...",
"created_at": "2026-03-15T14:05:00Z",
"message": "Draft merged into canonical memory",
"superseded_memory_id": "mem_xyz789"
}

Errors

StatusCodeCause
404drafts.not_foundDraft does not exist
404memories.not_foundSuperseding target not found
409drafts.invalid_stateDraft is not in draft status (already merged or rejected)
403memory_limit_reachedMemory limit for your plan reached

Reject Draft

POST /v1/memory-drafts/{draft_id}/reject

Reject a draft without mutating canonical memory.

Path parameters

ParameterTypeDescription
draft_idstringDraft identifier

Request body

ParameterTypeRequiredDefaultDescription
reasonstringNoReason for rejection

Examples

result = nx.reject_draft("draft_a1b2c3d4", reason="Duplicate of existing memory")
print(f"Status: {result['status']}")

Errors

StatusCodeCause
404drafts.not_foundDraft does not exist
409drafts.invalid_stateDraft is not in draft status

Get Branch

GET /v1/memory-branches/{branch_id}

Get grouped review information for a branch/session of drafts, including draft counts by status and merge recommendations.

Path parameters

ParameterTypeDescription
branch_idstringBranch/session identifier

Response fields

FieldTypeDescription
branch_idstringBranch identifier
total_draftsnumberTotal drafts in this branch
open_draftsnumberDrafts still in draft status
merged_draftsnumberMerged drafts
rejected_draftsnumberRejected drafts
recommendationsobjectRecommendation counts (e.g., {"merge_new": 2, "check_duplicate": 1})
draftsarrayAll draft objects in this branch

Examples

branch = nx.memory_branch("session-2026-03-15")
print(f"Open: {branch['open_drafts']}, Merged: {branch['merged_drafts']}")
print(f"Recommendations: {branch['recommendations']}")

Errors

StatusCodeCause
404drafts.branch_not_foundNo drafts found for this branch

Merge Branch

POST /v1/memory-branches/{branch_id}/merge

Merge all open drafts in a branch into canonical memory in a single operation.

Path parameters

ParameterTypeDescription
branch_idstringBranch/session identifier

Response fields

FieldTypeDescription
branch_idstringBranch identifier
merged_countnumberNumber of drafts merged
merged_memory_idsstring[]IDs of the created canonical memories

Examples

result = nx.merge_branch("session-2026-03-15")
print(f"Merged {result['merged_count']} drafts")
for mid in result["merged_memory_ids"]:
print(f" -> {mid}")

Response

{
"branch_id": "session-2026-03-15",
"merged_count": 3,
"merged_memory_ids": ["mem_001", "mem_002", "mem_003"]
}

Errors

StatusCodeCause
404drafts.branch_not_foundNo open drafts found for this branch

Reject Branch

POST /v1/memory-branches/{branch_id}/reject

Reject all open drafts in a branch.

Path parameters

ParameterTypeDescription
branch_idstringBranch/session identifier

Request body

ParameterTypeRequiredDefaultDescription
reasonstringNoReason for rejection

Examples

result = nx.reject_branch(
"session-2026-03-15",
reason="Session contained hallucinated memories"
)
print(f"Rejected {result['rejected_drafts']} drafts")

Errors

StatusCodeCause
404drafts.branch_not_foundNo open drafts found for this branch