---
title: "suggest an addition, change or removal of a kinship edge - any logged-in user, same as suggesting a person edit. Goes into the pending queue; does not change anything. (Auth)"
method: POST
path: "/api/people/relationeditsuggestion"
tags: ["GanjoorRelatedPerson"]
---

# suggest an addition, change or removal of a kinship edge - any logged-in user, same as suggesting a person edit. Goes into the pending queue; does not change anything. (Auth)

`POST /api/people/relationeditsuggestion`

## Request

- Base URL: `https://api.ganjoor.net` (the document declares no server; this is the origin it was published from)
- URL: `https://api.ganjoor.net/api/people/relationeditsuggestion`
- Auth: API key in header `Authorization`

## Request body

- GanjoorPersonRelationEditSuggestion: a contributor's suggestion to add a brand new kinship edge between two already-approved people, change an existing edge's type/degree/note, or remove an existing edge outright - the moderated counterpart of GanjoorPersonEditSuggestion, but for the edges between people rather than one person's own fields. Reviewed the same way (see GanjoorRelatedPersonService.ModeratePersonRelationEditSuggestionAsync).
  - `id` integer: suggestion id
  - `action` 0 | 1 | 2 | 3 | 4: what a GanjoorPersonRelationEditSuggestion proposes doing to the kinship graph
  - `kind` 0 | 1: which kind of edge a GanjoorPersonRelationEditSuggestion is about: a kinship tie (GanjoorPersonRelation, via SuggestedRelationType/ExistingRelationId) or a non-family affiliation tie (GanjoorPersonAffiliation, via SuggestedAffiliationType/ExistingAffiliationId). Defaults to Family (0), so every suggestion row created before this field existed keeps being read exactly as it was before - a plain kinship suggestion.
  - `existingRelationId` integer, nullable: for Modify/Remove when Kind is Family: the existing GanjoorPersonRelation row this suggestion targets. Null for Add, where there's nothing existing yet, and for any Kind == Affiliation suggestion (see ExistingAffiliationId instead).
  - `existingRelation` GanjoorPersonRelation: an approved kinship edge between two people (GanjoorRelatedPerson rows) - the live/materialized counterpart of a pending suggestion, which travels as JSON on GanjoorPoemGeoDateTagCorrection's SuggestedPersonGraphJson until it's approved and turned into rows here. The graph as a whole (all rows in this table) is a general kinship graph, not a strict tree - see PersonRelationType.
    - `id` integer: record id
    - `person1Id` integer: first person in the relation - for a directional relation type (Parent, Ancestor) this is the parent/ancestor side; for a symmetric one (Sibling, Spouse) order doesn't matter
    - `person1` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `person2Id` integer: second person in the relation - for a directional relation type (Parent, Ancestor) this is the child/descendant side
    - `person2` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `relationType` 0 | 1 | 2 | 3: the kind of kinship edge a GanjoorPersonRelation represents between two GanjoorRelatedPerson rows. The graph is not a strict tree - siblings can be known without their parents, an ancestor can be known without the exact number of generations in between, etc. - so this is a small, deliberately open set rather than a rigid parent/child-only model.
    - `degreeHint` integer, nullable: for RelationType == Ancestor, an optional known exact degree (e.g. 2 for "grandparent", 3 for "great-grandparent") - left null when only the relative order is known, not the exact number of generations in between. Not used for other relation types.
    - `note` string, nullable: free-text note (e.g. sourcing/reasoning for this relation)
    - `evidence` GanjoorPersonRelationEvidenceInfo[], nullable: evidence couplets for this relation - filled in only by GetRelationByIdAsync (not a column; the stored rows are GanjoorPersonRelationEvidence)
      - `id` integer: evidence row id (what a RemoveEvidence suggestion refers to)
      - `poemId` integer: poem id
      - `coupletIndex` integer: couplet index inside the poem
      - `coupletText` string, nullable: couplet text snapshot
      - `masterCatId` integer: master category id (book) the poem belongs to
      - `masterCatTitle` string, nullable: master category title (filled by the server for display)
      - `inferred` boolean: true if guessed by a backfill rather than attached by a person (does not count for per-book filtering)
  - `existingAffiliationId` integer, nullable: for Modify/Remove when Kind is Affiliation: the existing GanjoorPersonAffiliation row this suggestion targets. Null for Add, and for any Kind == Family suggestion (see ExistingRelationId instead).
  - `existingAffiliation` GanjoorPersonAffiliation: an approved non-family tie between two people (GanjoorRelatedPerson rows) - e.g. a minister serving a king. Kept separate from GanjoorPersonRelation (kinship) rather than folded into it: this is what lets two unrelated family trees show up as adjacent to each other (e.g. browsing the Barmakid tree surfaces a link out to the Abbasid tree via a shared minister) without treating "family tree" as its own entity to be linked - the tie is between the two people, and the tree-to-tree adjacency is just what falls out of rendering it that way.
    - `id` integer: record id
    - `person1Id` integer: first person in the tie - for a directional type (Minister, Advisor, Courtier, Patron) this is the one in the subordinate/serving role; for a symmetric one (Ally, Rival) order doesn't matter
    - `person1` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `person2Id` integer: second person in the tie - for a directional type this is the one being served
    - `person2` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `affiliationType` 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 99: the kind of non-family tie a GanjoorPersonAffiliation represents between two people - e.g. a minister serving a king, an advisor, a patron. Unlike PersonRelationType this has nothing to do with kinship; it's what lets two otherwise unrelated family trees show up as adjacent (e.g. "some Barmakids served the Abbasid court") without merging them into one tree. New values can be appended safely later (stored as int) as more cases come up.
    - `note` string, nullable: free-text note (e.g. sourcing/reasoning, or what the tie actually is when AffiliationType is Other)
    - `evidence` GanjoorPersonRelationEvidenceInfo[], nullable: optional couplets attesting this tie, filled when read through the service (not a mapped column; the stored rows are GanjoorPersonAffiliationEvidence)
      - `id` integer: evidence row id (what a RemoveEvidence suggestion refers to)
      - `poemId` integer: poem id
      - `coupletIndex` integer: couplet index inside the poem
      - `coupletText` string, nullable: couplet text snapshot
      - `masterCatId` integer: master category id (book) the poem belongs to
      - `masterCatTitle` string, nullable: master category title (filled by the server for display)
      - `inferred` boolean: true if guessed by a backfill rather than attached by a person (does not count for per-book filtering)
  - `person1Id` integer: the pair this suggestion is about. For Add, this and Person2Id define the new relation. For Modify/Remove, these are copied from ExistingRelation at submission time, so the suggestion always accurately reflects what it targets even if the underlying relation changes again before this is reviewed.
  - `person1` GanjoorRelatedPerson: a person refered by a poem
    - `id` integer: id
    - `name` string, nullable: name
    - `description` string, nullable: description
    - `wikiUrl` string, nullable: wikipedia url
    - `birthYearInLHijri` integer: birth year in lunar hijri
    - `deathYearInLHijri` integer: death year in lunar hijri
    - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
    - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
    - `birthLocationId` integer, nullable: birth location id
    - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `deathLocationId` integer, nullable: death location id
    - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `machineGenerated` boolean: AI generated
    - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
    - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
  - `person2Id` integer: second person id, see Person1Id
  - `person2` GanjoorRelatedPerson: a person refered by a poem
    - `id` integer: id
    - `name` string, nullable: name
    - `description` string, nullable: description
    - `wikiUrl` string, nullable: wikipedia url
    - `birthYearInLHijri` integer: birth year in lunar hijri
    - `deathYearInLHijri` integer: death year in lunar hijri
    - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
    - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
    - `birthLocationId` integer, nullable: birth location id
    - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `deathLocationId` integer, nullable: death location id
    - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `machineGenerated` boolean: AI generated
    - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
    - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
  - `suggestedRelationType` 0 | 1 | 2 | 3: the kind of kinship edge a GanjoorPersonRelation represents between two GanjoorRelatedPerson rows. The graph is not a strict tree - siblings can be known without their parents, an ancestor can be known without the exact number of generations in between, etc. - so this is a small, deliberately open set rather than a rigid parent/child-only model.
  - `confirmedExtraParent` boolean: only meaningful when SuggestedRelationType is Parent. Normally, a third Parent edge for the same child is rejected (familytree.js's buildLayout only ever attaches the first two it sorts to the front and silently drops the rest). Set this to true only when a third recorded parent is genuinely intended (e.g. competing genealogy traditions for the same legendary figure) to add/keep it anyway.
  - `reverseDirection` boolean: only meaningful for a Modify of a directional edge (Parent/Ancestor kinship, or any non-symmetric affiliation type): when true, approving the suggestion swaps the existing edge's Person1Id/Person2Id (e.g. fixes a Killer edge recorded the wrong way round) instead of the submitter having to delete it and add a new one. Person1Id/Person2Id of this suggestion itself stay those of the existing edge at submission time.
  - `suggestedAffiliationType` 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 99: the kind of non-family tie a GanjoorPersonAffiliation represents between two people - e.g. a minister serving a king, an advisor, a patron. Unlike PersonRelationType this has nothing to do with kinship; it's what lets two otherwise unrelated family trees show up as adjacent (e.g. "some Barmakids served the Abbasid court") without merging them into one tree. New values can be appended safely later (stored as int) as more cases come up.
  - `suggestedDegreeHint` integer, nullable: see GanjoorPersonRelation.DegreeHint - same for/Add/Modify/Remove-display purpose as SuggestedRelationType above
  - `suggestedNote` string, nullable: see GanjoorPersonRelation.Note - same Add/Modify/Remove-display purpose as SuggestedRelationType above
  - `evidencePoemId` integer, nullable: evidence couplet (see GanjoorPersonRelationEvidence): the poem it is in. Required for Action == AddEvidence; optional for Action == Add (the evidence is attached to the new relation when the suggestion is approved). Ignored otherwise.
  - `evidenceCoupletIndex` integer, nullable: evidence couplet index inside EvidencePoemId's poem
  - `evidenceCoupletText` string, nullable: snapshot of the evidence couplet's text, filled in by the server at submission time so the moderator sees exactly what the suggester pointed at
  - `existingEvidenceId` integer, nullable: for Action == RemoveEvidence: the GanjoorPersonRelationEvidence row to detach (a plain id, not an FK, so deleting that row never conflicts with old suggestions that mention it)
  - `suggestionNote` string, nullable: suggester's note to the moderator (e.g. why an existing relation is wrong)
  - `date` string, date-time: date
  - `userId` string, uuid: suggester's user id
  - `user` RAppUser: Application User
    - `id` string, uuid
    - `userName` string, nullable
    - `normalizedUserName` string, nullable
    - `email` string, nullable
    - `normalizedEmail` string, nullable
    - `emailConfirmed` boolean
    - `passwordHash` string, nullable
    - `securityStamp` string, nullable
    - `concurrencyStamp` string, nullable
    - `phoneNumber` string, nullable
    - `phoneNumberConfirmed` boolean
    - `twoFactorEnabled` boolean
    - `lockoutEnd` string, date-time, nullable
    - `lockoutEnabled` boolean
    - `accessFailedCount` integer
    - `firstName` string, nullable: First Name
    - `surName` string, nullable: Sur Name
    - `createDate` string, date-time, required: User Creation Date
    - `status` 0 | 1, required: User Status
    - `rImage` RImage: Image Files
      - `id` string, uuid: Id
      - `originalFileName` string, nullable: Original File Name
      - `contentType` string, nullable: content type
      - `fileSizeInBytes` integer: Original Image File Size In Byte
      - `imageWidth` integer: Original Image Width
      - `imageHeight` integer: Original Image Height
      - `folderName` string, nullable: پوشه محل ذخیره تصویر
      - `storedFileName` string, nullable: نام فایل ذخیره شده با بالاترین کیفیت
      - `dataTime` string, date-time: datetime
      - `lastModified` string, date-time: Last Modified for caching purposes
    - `rImageId` string, uuid, nullable: user image id
    - `nickName` string, nullable: nick name
    - `bio` string, nullable: biography
    - `website` string, nullable: web site
    - `screenName` string, nullable: user screen name
    - `lockoutMessage` string, nullable: if user is locked out purposely as some kind of temporary punishment put the message for him or her here
  - `reviewed` boolean: reviewed
  - `result` 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18: correction review result
  - `reviewDate` string, date-time: review date
  - `reviewNote` string, nullable: review note
  - `reviewerUserId` string, uuid, nullable: reviewer's user id
  - `reviewerUser` RAppUser: Application User
    - `id` string, uuid
    - `userName` string, nullable
    - `normalizedUserName` string, nullable
    - `email` string, nullable
    - `normalizedEmail` string, nullable
    - `emailConfirmed` boolean
    - `passwordHash` string, nullable
    - `securityStamp` string, nullable
    - `concurrencyStamp` string, nullable
    - `phoneNumber` string, nullable
    - `phoneNumberConfirmed` boolean
    - `twoFactorEnabled` boolean
    - `lockoutEnd` string, date-time, nullable
    - `lockoutEnabled` boolean
    - `accessFailedCount` integer
    - `firstName` string, nullable: First Name
    - `surName` string, nullable: Sur Name
    - `createDate` string, date-time, required: User Creation Date
    - `status` 0 | 1, required: User Status
    - `rImage` RImage: Image Files
      - `id` string, uuid: Id
      - `originalFileName` string, nullable: Original File Name
      - `contentType` string, nullable: content type
      - `fileSizeInBytes` integer: Original Image File Size In Byte
      - `imageWidth` integer: Original Image Width
      - `imageHeight` integer: Original Image Height
      - `folderName` string, nullable: پوشه محل ذخیره تصویر
      - `storedFileName` string, nullable: نام فایل ذخیره شده با بالاترین کیفیت
      - `dataTime` string, date-time: datetime
      - `lastModified` string, date-time: Last Modified for caching purposes
    - `rImageId` string, uuid, nullable: user image id
    - `nickName` string, nullable: nick name
    - `bio` string, nullable: biography
    - `website` string, nullable: web site
    - `screenName` string, nullable: user screen name
    - `lockoutMessage` string, nullable: if user is locked out purposely as some kind of temporary punishment put the message for him or her here

## Response `200`

OK

- GanjoorPersonRelationEditSuggestion: a contributor's suggestion to add a brand new kinship edge between two already-approved people, change an existing edge's type/degree/note, or remove an existing edge outright - the moderated counterpart of GanjoorPersonEditSuggestion, but for the edges between people rather than one person's own fields. Reviewed the same way (see GanjoorRelatedPersonService.ModeratePersonRelationEditSuggestionAsync).
  - `id` integer: suggestion id
  - `action` 0 | 1 | 2 | 3 | 4: what a GanjoorPersonRelationEditSuggestion proposes doing to the kinship graph
  - `kind` 0 | 1: which kind of edge a GanjoorPersonRelationEditSuggestion is about: a kinship tie (GanjoorPersonRelation, via SuggestedRelationType/ExistingRelationId) or a non-family affiliation tie (GanjoorPersonAffiliation, via SuggestedAffiliationType/ExistingAffiliationId). Defaults to Family (0), so every suggestion row created before this field existed keeps being read exactly as it was before - a plain kinship suggestion.
  - `existingRelationId` integer, nullable: for Modify/Remove when Kind is Family: the existing GanjoorPersonRelation row this suggestion targets. Null for Add, where there's nothing existing yet, and for any Kind == Affiliation suggestion (see ExistingAffiliationId instead).
  - `existingRelation` GanjoorPersonRelation: an approved kinship edge between two people (GanjoorRelatedPerson rows) - the live/materialized counterpart of a pending suggestion, which travels as JSON on GanjoorPoemGeoDateTagCorrection's SuggestedPersonGraphJson until it's approved and turned into rows here. The graph as a whole (all rows in this table) is a general kinship graph, not a strict tree - see PersonRelationType.
    - `id` integer: record id
    - `person1Id` integer: first person in the relation - for a directional relation type (Parent, Ancestor) this is the parent/ancestor side; for a symmetric one (Sibling, Spouse) order doesn't matter
    - `person1` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `person2Id` integer: second person in the relation - for a directional relation type (Parent, Ancestor) this is the child/descendant side
    - `person2` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `relationType` 0 | 1 | 2 | 3: the kind of kinship edge a GanjoorPersonRelation represents between two GanjoorRelatedPerson rows. The graph is not a strict tree - siblings can be known without their parents, an ancestor can be known without the exact number of generations in between, etc. - so this is a small, deliberately open set rather than a rigid parent/child-only model.
    - `degreeHint` integer, nullable: for RelationType == Ancestor, an optional known exact degree (e.g. 2 for "grandparent", 3 for "great-grandparent") - left null when only the relative order is known, not the exact number of generations in between. Not used for other relation types.
    - `note` string, nullable: free-text note (e.g. sourcing/reasoning for this relation)
    - `evidence` GanjoorPersonRelationEvidenceInfo[], nullable: evidence couplets for this relation - filled in only by GetRelationByIdAsync (not a column; the stored rows are GanjoorPersonRelationEvidence)
      - `id` integer: evidence row id (what a RemoveEvidence suggestion refers to)
      - `poemId` integer: poem id
      - `coupletIndex` integer: couplet index inside the poem
      - `coupletText` string, nullable: couplet text snapshot
      - `masterCatId` integer: master category id (book) the poem belongs to
      - `masterCatTitle` string, nullable: master category title (filled by the server for display)
      - `inferred` boolean: true if guessed by a backfill rather than attached by a person (does not count for per-book filtering)
  - `existingAffiliationId` integer, nullable: for Modify/Remove when Kind is Affiliation: the existing GanjoorPersonAffiliation row this suggestion targets. Null for Add, and for any Kind == Family suggestion (see ExistingRelationId instead).
  - `existingAffiliation` GanjoorPersonAffiliation: an approved non-family tie between two people (GanjoorRelatedPerson rows) - e.g. a minister serving a king. Kept separate from GanjoorPersonRelation (kinship) rather than folded into it: this is what lets two unrelated family trees show up as adjacent to each other (e.g. browsing the Barmakid tree surfaces a link out to the Abbasid tree via a shared minister) without treating "family tree" as its own entity to be linked - the tie is between the two people, and the tree-to-tree adjacency is just what falls out of rendering it that way.
    - `id` integer: record id
    - `person1Id` integer: first person in the tie - for a directional type (Minister, Advisor, Courtier, Patron) this is the one in the subordinate/serving role; for a symmetric one (Ally, Rival) order doesn't matter
    - `person1` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `person2Id` integer: second person in the tie - for a directional type this is the one being served
    - `person2` GanjoorRelatedPerson: a person refered by a poem
      - `id` integer: id
      - `name` string, nullable: name
      - `description` string, nullable: description
      - `wikiUrl` string, nullable: wikipedia url
      - `birthYearInLHijri` integer: birth year in lunar hijri
      - `deathYearInLHijri` integer: death year in lunar hijri
      - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
      - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
      - `birthLocationId` integer, nullable: birth location id
      - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `deathLocationId` integer, nullable: death location id
      - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
        - `id` integer: id
        - `name` string, nullable: name
        - `latitude` number, double: Latitude
        - `longitude` number, double: Longitude
        - `machineGenerated` boolean: AI generated
      - `machineGenerated` boolean: AI generated
      - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
      - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
      - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
    - `affiliationType` 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 99: the kind of non-family tie a GanjoorPersonAffiliation represents between two people - e.g. a minister serving a king, an advisor, a patron. Unlike PersonRelationType this has nothing to do with kinship; it's what lets two otherwise unrelated family trees show up as adjacent (e.g. "some Barmakids served the Abbasid court") without merging them into one tree. New values can be appended safely later (stored as int) as more cases come up.
    - `note` string, nullable: free-text note (e.g. sourcing/reasoning, or what the tie actually is when AffiliationType is Other)
    - `evidence` GanjoorPersonRelationEvidenceInfo[], nullable: optional couplets attesting this tie, filled when read through the service (not a mapped column; the stored rows are GanjoorPersonAffiliationEvidence)
      - `id` integer: evidence row id (what a RemoveEvidence suggestion refers to)
      - `poemId` integer: poem id
      - `coupletIndex` integer: couplet index inside the poem
      - `coupletText` string, nullable: couplet text snapshot
      - `masterCatId` integer: master category id (book) the poem belongs to
      - `masterCatTitle` string, nullable: master category title (filled by the server for display)
      - `inferred` boolean: true if guessed by a backfill rather than attached by a person (does not count for per-book filtering)
  - `person1Id` integer: the pair this suggestion is about. For Add, this and Person2Id define the new relation. For Modify/Remove, these are copied from ExistingRelation at submission time, so the suggestion always accurately reflects what it targets even if the underlying relation changes again before this is reviewed.
  - `person1` GanjoorRelatedPerson: a person refered by a poem
    - `id` integer: id
    - `name` string, nullable: name
    - `description` string, nullable: description
    - `wikiUrl` string, nullable: wikipedia url
    - `birthYearInLHijri` integer: birth year in lunar hijri
    - `deathYearInLHijri` integer: death year in lunar hijri
    - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
    - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
    - `birthLocationId` integer, nullable: birth location id
    - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `deathLocationId` integer, nullable: death location id
    - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `machineGenerated` boolean: AI generated
    - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
    - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
  - `person2Id` integer: second person id, see Person1Id
  - `person2` GanjoorRelatedPerson: a person refered by a poem
    - `id` integer: id
    - `name` string, nullable: name
    - `description` string, nullable: description
    - `wikiUrl` string, nullable: wikipedia url
    - `birthYearInLHijri` integer: birth year in lunar hijri
    - `deathYearInLHijri` integer: death year in lunar hijri
    - `validBirthDate` boolean: BirthYearInLHijri, for some poets it is only indicator of their century of birth and should not be relied as their valid birth date
    - `validDeathDate` boolean: DeathYearInLHijri, for some poets it is only indicator of their century of death and should not be relied as their valid death date
    - `birthLocationId` integer, nullable: birth location id
    - `birthLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `deathLocationId` integer, nullable: death location id
    - `deathLocation` GanjoorGeoLocation: Geo Locations (Cities) referred by Ganjoor Metadata
      - `id` integer: id
      - `name` string, nullable: name
      - `latitude` number, double: Latitude
      - `longitude` number, double: Longitude
      - `machineGenerated` boolean: AI generated
    - `machineGenerated` boolean: AI generated
    - `familyTreeCaption` string, nullable: optional caption for the family tree this person is treated as the root of (e.g. "ساسانیان", "آل برمک") - purely a display label for whoever a tree is being browsed from; nothing enforces that this person actually has no recorded ancestors themselves, and most people will leave this null (only whichever person a tree is "named after" needs one set).
    - `importance` 0 | 1 | 2: how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `gender` 0 | 1 | 2: a GanjoorRelatedPerson's gender - used only by the family-tree chart (familytree.js's buildLayout()) to break ties when a child has two recorded parents and both happen to have their own recorded ancestors (so neither side can be preferred on "continues a documented lineage" grounds alone): the father is preferred as the tree's spine parent, matching conventional patrilineal genealogy-chart practice, with the mother then drawn attached beside him. Never used to filter, rank or otherwise affect query results. New values can be appended safely later (stored as int).
    - `aliases` string, nullable: other names this person is known by (epithets, titles, alternative spellings), separated by the Persian comma (،) - e.g. for رستم: «تهمتن، رستم دستان، پور زال». Used by the AI tagging jobs to match a name in a poem to this person, and filled automatically when two people are merged
  - `suggestedRelationType` 0 | 1 | 2 | 3: the kind of kinship edge a GanjoorPersonRelation represents between two GanjoorRelatedPerson rows. The graph is not a strict tree - siblings can be known without their parents, an ancestor can be known without the exact number of generations in between, etc. - so this is a small, deliberately open set rather than a rigid parent/child-only model.
  - `confirmedExtraParent` boolean: only meaningful when SuggestedRelationType is Parent. Normally, a third Parent edge for the same child is rejected (familytree.js's buildLayout only ever attaches the first two it sorts to the front and silently drops the rest). Set this to true only when a third recorded parent is genuinely intended (e.g. competing genealogy traditions for the same legendary figure) to add/keep it anyway.
  - `reverseDirection` boolean: only meaningful for a Modify of a directional edge (Parent/Ancestor kinship, or any non-symmetric affiliation type): when true, approving the suggestion swaps the existing edge's Person1Id/Person2Id (e.g. fixes a Killer edge recorded the wrong way round) instead of the submitter having to delete it and add a new one. Person1Id/Person2Id of this suggestion itself stay those of the existing edge at submission time.
  - `suggestedAffiliationType` 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 99: the kind of non-family tie a GanjoorPersonAffiliation represents between two people - e.g. a minister serving a king, an advisor, a patron. Unlike PersonRelationType this has nothing to do with kinship; it's what lets two otherwise unrelated family trees show up as adjacent (e.g. "some Barmakids served the Abbasid court") without merging them into one tree. New values can be appended safely later (stored as int) as more cases come up.
  - `suggestedDegreeHint` integer, nullable: see GanjoorPersonRelation.DegreeHint - same for/Add/Modify/Remove-display purpose as SuggestedRelationType above
  - `suggestedNote` string, nullable: see GanjoorPersonRelation.Note - same Add/Modify/Remove-display purpose as SuggestedRelationType above
  - `evidencePoemId` integer, nullable: evidence couplet (see GanjoorPersonRelationEvidence): the poem it is in. Required for Action == AddEvidence; optional for Action == Add (the evidence is attached to the new relation when the suggestion is approved). Ignored otherwise.
  - `evidenceCoupletIndex` integer, nullable: evidence couplet index inside EvidencePoemId's poem
  - `evidenceCoupletText` string, nullable: snapshot of the evidence couplet's text, filled in by the server at submission time so the moderator sees exactly what the suggester pointed at
  - `existingEvidenceId` integer, nullable: for Action == RemoveEvidence: the GanjoorPersonRelationEvidence row to detach (a plain id, not an FK, so deleting that row never conflicts with old suggestions that mention it)
  - `suggestionNote` string, nullable: suggester's note to the moderator (e.g. why an existing relation is wrong)
  - `date` string, date-time: date
  - `userId` string, uuid: suggester's user id
  - `user` RAppUser: Application User
    - `id` string, uuid
    - `userName` string, nullable
    - `normalizedUserName` string, nullable
    - `email` string, nullable
    - `normalizedEmail` string, nullable
    - `emailConfirmed` boolean
    - `passwordHash` string, nullable
    - `securityStamp` string, nullable
    - `concurrencyStamp` string, nullable
    - `phoneNumber` string, nullable
    - `phoneNumberConfirmed` boolean
    - `twoFactorEnabled` boolean
    - `lockoutEnd` string, date-time, nullable
    - `lockoutEnabled` boolean
    - `accessFailedCount` integer
    - `firstName` string, nullable: First Name
    - `surName` string, nullable: Sur Name
    - `createDate` string, date-time, required: User Creation Date
    - `status` 0 | 1, required: User Status
    - `rImage` RImage: Image Files
      - `id` string, uuid: Id
      - `originalFileName` string, nullable: Original File Name
      - `contentType` string, nullable: content type
      - `fileSizeInBytes` integer: Original Image File Size In Byte
      - `imageWidth` integer: Original Image Width
      - `imageHeight` integer: Original Image Height
      - `folderName` string, nullable: پوشه محل ذخیره تصویر
      - `storedFileName` string, nullable: نام فایل ذخیره شده با بالاترین کیفیت
      - `dataTime` string, date-time: datetime
      - `lastModified` string, date-time: Last Modified for caching purposes
    - `rImageId` string, uuid, nullable: user image id
    - `nickName` string, nullable: nick name
    - `bio` string, nullable: biography
    - `website` string, nullable: web site
    - `screenName` string, nullable: user screen name
    - `lockoutMessage` string, nullable: if user is locked out purposely as some kind of temporary punishment put the message for him or her here
  - `reviewed` boolean: reviewed
  - `result` 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18: correction review result
  - `reviewDate` string, date-time: review date
  - `reviewNote` string, nullable: review note
  - `reviewerUserId` string, uuid, nullable: reviewer's user id
  - `reviewerUser` RAppUser: Application User
    - `id` string, uuid
    - `userName` string, nullable
    - `normalizedUserName` string, nullable
    - `email` string, nullable
    - `normalizedEmail` string, nullable
    - `emailConfirmed` boolean
    - `passwordHash` string, nullable
    - `securityStamp` string, nullable
    - `concurrencyStamp` string, nullable
    - `phoneNumber` string, nullable
    - `phoneNumberConfirmed` boolean
    - `twoFactorEnabled` boolean
    - `lockoutEnd` string, date-time, nullable
    - `lockoutEnabled` boolean
    - `accessFailedCount` integer
    - `firstName` string, nullable: First Name
    - `surName` string, nullable: Sur Name
    - `createDate` string, date-time, required: User Creation Date
    - `status` 0 | 1, required: User Status
    - `rImage` RImage: Image Files
      - `id` string, uuid: Id
      - `originalFileName` string, nullable: Original File Name
      - `contentType` string, nullable: content type
      - `fileSizeInBytes` integer: Original Image File Size In Byte
      - `imageWidth` integer: Original Image Width
      - `imageHeight` integer: Original Image Height
      - `folderName` string, nullable: پوشه محل ذخیره تصویر
      - `storedFileName` string, nullable: نام فایل ذخیره شده با بالاترین کیفیت
      - `dataTime` string, date-time: datetime
      - `lastModified` string, date-time: Last Modified for caching purposes
    - `rImageId` string, uuid, nullable: user image id
    - `nickName` string, nullable: nick name
    - `bio` string, nullable: biography
    - `website` string, nullable: web site
    - `screenName` string, nullable: user screen name
    - `lockoutMessage` string, nullable: if user is locked out purposely as some kind of temporary punishment put the message for him or her here

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `403`: Forbidden

## Changes

- **2026-10-13** `afb4560919ee`: 2 warning, 6 info
  - added the new `15.00` enum value to the `existingAffiliation/affiliationType` response property for the response status `200`
  - added the new `15.00` enum value to the `suggestedAffiliationType` response property for the response status `200`
  - added the new `15.00` enum value to the request property `existingAffiliation/affiliationType` (media type: application/json)
  - added the new `15.00` enum value to the request property `existingAffiliation/affiliationType` (media type: text/json)
  - …4 more
- **2026-10-10** `330e7bb195c1`: 24 info
  - added the new optional request property `existingAffiliation/person1/aliases` (media type: application/json)
  - added the new optional request property `existingAffiliation/person1/aliases` (media type: text/json)
  - added the new optional request property `existingAffiliation/person1/aliases` (media type: application/*+json)
  - added the new optional request property `existingAffiliation/person2/aliases` (media type: application/json)
  - …20 more
- **2026-10-10** `bb399e082b6e`: 4 info
  - added the new optional request property `reverseDirection` (media type: application/json)
  - added the new optional request property `reverseDirection` (media type: text/json)
  - added the new optional request property `reverseDirection` (media type: application/*+json)
  - added the optional property `reverseDirection` to the response with the `200` status
- …earlier changes not shown

[Operation history](https://skmtc.dev/ganjoor/apis/rmuseum-api/changes/api/people/relationeditsuggestion/post.md)

---

[API](https://skmtc.dev/ganjoor/apis/rmuseum-api.md) - [All operations](https://skmtc.dev/ganjoor/apis/rmuseum-api/llms.txt) - [OpenAPI document](https://skmtc.dev/ganjoor/apis/rmuseum-api/revisions/143b912b1085?raw)
