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
observationstringYes—Memory content
contextstringNo—Additional context
context_idsstring[]No[]Linked context IDs
agent_idstringNo—Agent identifier
tagsstring[]No[]Tags for categorization
importancenumberNo5Importance score (1-10)
confidencenumberNo1.0Confidence score (0-1)
space_idstringNo—Context space to store into
branch_idstringNo—Branch/session identifier for grouping drafts
ttl_secondsnumberNo—Time-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
statusstringNo—Filter by status: draft, merged, or rejected
branch_idstringNo—Filter 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_tostringNo—Existing 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_idstringNo—Existing 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
reasonstringNo—Reason 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
reasonstringNo—Reason 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