Accept an edge proposal
Accept a proposal: promote it to a real KG edge.
graph_edges (edge_id = proposal_id, is_approved = TRUE, all
cross-domain fields preserved) and the proposal row is deleted, atomically in
one transaction. The edge carries created_by = api:<caller> for audit. A proposal id that is not the subject’s (cross-org,
cross-user, or already consumed) returns 404 proposal_not_found.
Acceptance SUPERSEDES a prior rejection: any proposal_rejections row for
this pair (either direction) is cleared in the same transaction, so a pair
you once rejected and later accepted is not silently blocked from being
re-suggested if the edge is removed again.
Brain staleness is not stamped on brain_metadata: its status CHECK
allows only none|queued|training|ready|failed (no stale) and writing
it would also corrupt the in-flight-train guard. The new edge instead changes
the org’s live graph_edges count, which the ExoBrain compares against its
exported N/E on every query — so /v1/retrieve honestly flips to
degradationReason="brain_stale" on the next call, no metadata flag needed.
Authorization
This route requires thewrite scope.
A missing or invalid credential returns 401 authentication_error. A valid credential without the scope returns 403 permission_denied, and the problem body names the exact scope required.
Headers
These are request conventions the contract does not declare as parameters, so they do not appear in the schema tables below.Success responses
Errors
proposal_not_found, idempotency_in_flight, idempotency_key_reuse. Every problem body names its own code, and the type URI always resolves to the matching page.
Every error is an RFC 9457 application/problem+json body carrying a stable code, a requestId, and a suggestedAction where Exo has one. See Errors.Authorizations
An Exo API key (exo_...) sent as the X-Exo-API-Key header.
Path Parameters
The proposal id from GET /v1/proposals.
Response
Successful Response
The outcome of an accept/reject on a proposal.
decision is accepted or rejected. edgeId is the id of the
edge created by an accept (equal to the proposal id); null on reject.
rejectionRecorded reports whether the reject was written to the durable
rejection ledger, i.e. whether this pair is now permanently excluded from
re-suggestion rather than merely removed from the queue. It is null on
accept (which has nothing to record) and false only on a deployment whose
schema predates the ledger.