---
title: "Approve or reject a detected edge"
method: PATCH
path: "/pots/{pot_id}/edges/{edge_id}"
tags: ["edges"]
---

# Approve or reject a detected edge

`PATCH /pots/{pot_id}/edges/{edge_id}`

## Request

- Base URL: `https://api.scipot.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.scipot.ai/pots/{pot_id}/edges/{edge_id}`
- Auth: none declared
- TypeScript: <https://skmtc.dev/scipot/apis/scipot-core-api/docs/pots/:pot_id/edges/:edge_id/patch.ts> (a function with typed inputs; its response is checked against the schema)

A curator decision on one `supersedes` edge. Other edge types are refused. `validated` lets the repeal retire the older fact and bypasses the Supported floor: a curator may let a weaker fact retire a stronger one. `auto-approved` (verification skipped, or the second pass passed) does not bypass that floor. `rejected` leaves the older fact current. The caller and the time are stored on the edge. Other fields of `EdgeUpdate` are refused: this is the decision, not an editor.

With verification skipped (the production default) a detected repeal is already `auto-approved`. When verification runs and rejects it, the edge stays `pending`, and a later rejection demotes an earlier `auto-approved` repeal to `pending`. A curator's `validated` or `rejected` is not overwritten. Pending repeals are listed on `POST /pots/{pot_id}/query` (`edges_context.pending_supersedes`) when edges are included. An `auto-approved` repeal the Supported floor or an axiom stops is listed as `edges_context.blocked_supersedes` (and on `GET /edges` as `blocked_supersedes`) with a reason, so a curator can still `validated` it past the floor. Saving a repeal also stores a `contradicts` edge in the same direction unless the pair already has one, so contradiction surfaces show the conflict.

## Path parameters

- `pot_id` string, uuid, required
- `edge_id` string, uuid, required

## Request body

- EdgeUpdate: Edge update request schema.
  - `type` 'supports' | 'contradicts' | 'resolved_contradiction' | 'refines' | 'derives_from' | 'is_part_of' | 'extends' | 'cites' | 'related_to' | 'supersedes' | 'restates': Types of semantic relationships between facts. M7 uses a simplified subset. Full taxonomy in edge-types.md will be implemented in M8+ when GraphRAG is introduced.
  - `weight` number, nullable
  - `rationale` string, nullable
  - `validation_status` string, nullable

## Response `200`

Edge updated

- Edge: Semantic relationship between two facts. Directionality: from_id → to_id Example: "Fact A supports Fact B" → Edge(from_id=A, to_id=B, type=SUPPORTS) This model is used for persistence and API responses. The workbench uses its own Edge model which will be unified in M8+.
  - `id` string, required: Unique edge identifier
  - `pot_id` string, uuid, required: POT this edge belongs to
  - `workspace_id` string, uuid, required: Workspace this edge belongs to
  - `from_id` string, uuid, required: Source fact ID (A in A → B)
  - `to_id` string, uuid, required: Target fact ID (B in A → B)
  - `type` 'supports' | 'contradicts' | 'resolved_contradiction' | 'refines' | 'derives_from' | 'is_part_of' | 'extends' | 'cites' | 'related_to' | 'supersedes' | 'restates', required: Types of semantic relationships between facts. M7 uses a simplified subset. Full taxonomy in edge-types.md will be implemented in M8+ when GraphRAG is introduced.
  - `weight` number, required: Confidence in relationship (0.0-1.0)
  - `rationale` string: Brief explanation of why this relationship exists
  - `detected_by` string: Detector/model that identified this edge
  - `companion_of` string, nullable: Supersedes edge id this contradicts edge was stored beside. Set only on the companion created with a repeal; a later run may rewrite that repeal's rationale without touching this tag.
  - `episode_id` string, nullable: The episode whose window created this edge (point 6, the seal): a POST /edges sent with X-Episode-ID, or the edges a sealed write's post-ingestion created. Set when the edge is created and never changed; a rerun that rewrites the edge keeps it. Never read by scoring or propagation.
  - `confidence` number: Detection confidence (0.0-1.0)
  - `judge` string, nullable: The near-duplicate judge that decided a restates edge, as <backend>:<model> (decisions:gpt-6-luna, bedrock:<model id>). Null on every other edge.
  - `partial` boolean, nullable: Restates edges since v0.7.105.0: true when the restating fact repeats only part of its target. The facts split from one passage were judged together against it, and the group repeats the target; each one alone gives only a piece. Still not independent evidence. Null on every other edge and on whole restatements.
  - `shared_figures` integer, nullable: Restates edges since v0.7.104.0: how many figures (amounts, dates, counts) the restating fact and its target both give. First key of the restates_all order. Null on every other edge and on older restates edges.
  - `similarity` number, nullable: Restates edges since v0.7.104.0: the similarity deduplication ranked the pair by (the higher of wording and meaning, 0-1). Second key of the restates_all order. Null on every other edge and on older restates edges.
  - `validation_status` string: Validation status (auto-approved, pending, validated, rejected)
  - `decided_by` string, nullable: Who approved or rejected a repeal: a contributor id, or integrator:<id>
  - `decided_at` string, date-time, nullable: When a curator approved or rejected a repeal
  - `resolution_type` string, nullable: Resolution type (confirmed_a, confirmed_b, both_valid, needs_update, dismissed)
  - `resolved_by_name` string, nullable: Name of the person who resolved the contradiction
  - `resolved_by_role` string, nullable: Role of the person who resolved the contradiction
  - `resolved_by_contributor_id` string, uuid, nullable: Contributor registry ID of the person who resolved the contradiction
  - `resolved_at` string, date-time, nullable: When the contradiction was resolved
  - `resolution_notes` string, nullable: Notes about the resolution
  - `resolution_score_shifts` object, nullable: POT Score delta actually applied to each fact by this resolution, keyed by fact id. Revert undoes these amounts.
  - `kind` 'express' | 'tacit' | 'partial' | 'hierarchy' | 'suspension' | 'extension' | 'manual', nullable: How the repeal came about: express, partial, tacit, hierarchy, manual, suspension or extension. An edge stored before 0.7.57.0 reads manual when a person created it (detected_by manual) and express otherwise.
  - `basis` string, nullable: What the repeal rests on: passage (the replacing fact's own text and document), curator, or rule:<id> (a precedence rule of the POT).
  - `scope_section` string, nullable: The section of the older document the repeal is limited to, when it cites one.
  - `effective_at` string, date, nullable: Day the repeal takes effect, when a person states it. Null = from the replacing fact's valid_from (or the day the edge was stored).
  - `resume_at` string, date, nullable: For a suspension: the day the older fact applies again. Null = open.
  - `applied_at` string, date-time, nullable: When the repeal started retiring its target in SciPot (transaction time). Written once: when the edge is stored as auto-approved, or when a curator first validates it. Never moved by a later decision.
  - `axiom_conflict` string[], nullable: Set when detection held this repeal for a curator because applying it may contradict an active constitution axiom or rule (the axiom veto, 0.7.88.0): the ids of those axioms, empty when the check could not name one (it failed or doubted). Null = never held. A re-detection never turns a held repeal back to auto-approved; a curator's validated lifts the hold.
  - `axiom_hold` 'conflict' | 'unverified', nullable: Why the axiom veto holds this repeal: `conflict` (an open contradiction with an axiom, or the check judged one, or doubted) stays until a curator validates the repeal; `unverified` (the check could not be completed: unreadable facts or edges, a timeout, an error, an ambiguous answer) is lifted by a later detection whose check answers clear. Null = never held; a hold stored without a reason reads as `conflict`.
  - `created_at` string, date-time, nullable
  - `updated_at` string, date-time, nullable

## Other responses

- `404`: POT or edge not found
- `422`: Not a validate/reject decision

## Changes

- **2026-10-24** `7764b32b043b`: 3 info
  - added the optional property `partial` to the response with the `200` status
  - added the optional property `shared_figures` to the response with the `200` status
  - added the optional property `similarity` to the response with the `200` status
- **2026-10-24** `0fb6ed61ba72`: 1 warning, 2 info
  - added the new `restates` enum value to the `type` response property for the response status `200`
  - added the new `restates` enum value to the request property `type/anyOf[subschema #1: EdgeType]/`
  - added the optional property `judge` to the response with the `200` status
- **2026-10-17** `193bc4a66351`: 2 info
  - added the optional property `axiom_conflict` to the response with the `200` status
  - added the optional property `axiom_hold` to the response with the `200` status
- **2026-10-14** `34be7df24641`: 7 info
  - added the optional property `applied_at` to the response with the `200` status
  - added the optional property `basis` to the response with the `200` status
  - added the optional property `effective_at` to the response with the `200` status
  - added the optional property `episode_id` to the response with the `200` status
  - …3 more
- **2026-10-12** `4ec43ee51b64`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/scipot/apis/scipot-core-api/changes/pots/:pot_id/edges/:edge_id/patch.md)

---

[API](https://skmtc.dev/scipot/apis/scipot-core-api.md) - [All operations](https://skmtc.dev/scipot/apis/scipot-core-api/llms.txt) - [OpenAPI document](https://skmtc.dev/scipot/apis/scipot-core-api/revisions/7620af82156a?raw)
