GraphQL API

Contact Erasure

Preview and permanently erase a contact for a right-to-erasure request

Erasure is not an update - it destroys a contact and the data attached to it, permanently, to satisfy a right-to-erasure request.

It is gated twice over:

RequirementDetail
ScopeCONTACTS_ERASE - deliberately separate from CONTACTS_MANAGE.
RoleOWNER, ADMIN or EDITOR. A key is held to the same role bar as a person.
OrgOrg-scoped. A caller can only erase contacts in the org it is acting on.

previewErase

previewErase(input: EraseInput!): ErasePreviewType!

CONTACTS_ERASE

A dry run. Changes nothing, and tells you exactly what the real call would destroy.

Arguments - input: EraseInput!

FieldTypeRequiredDescription
contactIdID!YesThe contact to erase.
organisationIdID!YesThe org it belongs to.
reasonStringNoWhy - recorded for your own audit trail.

Returns ErasePreviewType!

FieldTypeDescription
contactIdID!The contact the preview is for.
willErase[EraseCount!]!What would be destroyed, as { collection, count } pairs.
warnings[String!]!Anything you should see before proceeding. Empty when there is nothing to flag.

Example

query Preview {
  previewErase(input: {
    contactId: "6804f4d5a6f9f35f6e66f1c9"
    organisationId: "6716b2f0a1c34d0012ab89ef"
  }) {
    contactId
    willErase { collection count }
    warnings
  }
}
{
  "data": {
    "previewErase": {
      "contactId": "6804f4d5a6f9f35f6e66f1c9",
      "willErase": [
        { "collection": "messages", "count": 412 },
        { "collection": "chats", "count": 3 },
        { "collection": "fieldValues", "count": 11 }
      ],
      "warnings": []
    }
  }
}

Always call this first and show the result to a human. It is the only chance to notice that the wrong contact was selected, or that a merged contact carries far more history than expected.

eraseContact

eraseContact(input: EraseInput!): EraseResultType!

CONTACTS_ERASE

Performs the erasure. Same input as the preview.

Returns EraseResultType!

FieldTypeDescription
contactIdID!The contact that was erased.
erasedAtDateTime!When it happened.
affectedCounts[EraseCount!]!What was actually destroyed.
erasureLogIdID!The erasure record - keep it as your proof the request was fulfilled.

Example

mutation Erase {
  eraseContact(input: {
    contactId: "6804f4d5a6f9f35f6e66f1c9"
    organisationId: "6716b2f0a1c34d0012ab89ef"
    reason: "Subject access request #4471"
  }) {
    erasedAt
    erasureLogId
    affectedCounts { collection count }
  }
}

This is irreversible. There is no undo, and no equivalent of a merge split to put the data back. Compare affectedCounts against the preview's willErase - a mismatch means something changed between the two calls.

Store the erasureLogId and your reason on your side. The erasure record is what lets you answer "prove you deleted it" later, and reason is the only field carrying your own ticket reference into it.

A Safe Erase Flow

  1. previewErase - read willErase and warnings.
  2. Put the counts in front of a human and get an explicit confirmation.
  3. eraseContact with the same contactId and a reason naming your ticket.
  4. Record erasureLogId and erasedAt.

Do not skip step 2 in an automated pipeline. An erasure triggered by a bad contact ID cannot be walked back.

Was this page helpful?

On this page