Skip to main content

Deletion Rules

DokStamp is designed to preserve the integrity of issued certificates. Once a certificate has been issued, the entities it references are protected from deletion. This page explains the rules and the recommended patterns for integrations.

Soft deletes

All entities in DokStamp use soft deletes: when you call DELETE /resource/{uuid}, the record is not physically removed from the database. Instead, a deleted_at timestamp is set. Soft-deleted records:
  • Are excluded from all GET list responses
  • Cannot be referenced by new resources
  • Retain all their data and relationships intact
  • Can be restored by the platform administrator if needed
This means no data is ever permanently lost through the standard delete API.

Deletion protection rules

Attempting to delete an entity that has dependent issued certificates will result in an error. The protection is enforced at the database level via foreign key constraints.
EntityCannot be deleted when…
InstitutionHas any courses, modules, or certificates referencing it
CourseHas cohorts, enrollments, or certificates referencing it
ModuleIs attached to any course (course_modules relationship exists)
CohortHas enrollments or certificates referencing it
StudentHas certificates, enrollments, or a credential subject snapshot

Why this matters for integrations

When your third-party system deletes or deactivates an entity, you must not mirror that deletion blindly to DokStamp if certificates have already been issued. The recommended approach:

1. Check for certificates before deleting

async function canDelete(entityType, uuid) {
  const certs = await api.get('/certificates', {
    params: { [`where[${entityType}_uuid]`]: uuid, per_page: 1 }
  });
  return certs.data.meta.total === 0;
}

// Example for a course
if (await canDelete('course', courseUuid)) {
  await api.delete(`/courses/${courseUuid}`);
} else {
  // Archive instead of delete
  await api.patch(`/courses/${courseUuid}`, { status: 'archived' });
}

2. Archive instead of delete

For courses and institutions, prefer updating the status field to archived or inactive rather than deleting:
// Instead of DELETE /courses/{uuid}
await api.patch(`/courses/${courseUuid}`, { status: 'archived' });
This preserves all relationships while clearly marking the entity as no longer active.

3. Handle the error gracefully

If a deletion attempt fails due to an existing dependency, the API returns an error. Always handle this in your integration:
try {
  await api.delete(`/courses/${courseUuid}`);
} catch (err) {
  if (err.response?.status === 500) {
    // Likely a referential integrity error — log and archive instead
    logger.warn(`Cannot delete course ${courseUuid} — has dependent records. Archiving instead.`);
    await api.patch(`/courses/${courseUuid}`, { status: 'archived' });
  } else {
    throw err;
  }
}

Certificate immutability

Once a certificate is issued, all the data associated with it is preserved:
  • Student identity (name, email, document ID, date of birth, country) — captured in an immutable snapshot at issuance
  • Course, institution, cohort, and enrollment data — all preserved through the referential integrity rules above
This guarantees that a certificate’s public verification URL will always display accurate, complete information regardless of any future changes to the underlying entities.

Module detachment vs deletion

Modules can be detached from a course without being deleted:
DELETE /courses/{course_uuid}/detach/modules
This removes the course_modules association but leaves both the course and the module intact. This is the preferred operation when reorganizing a curriculum, as it allows the module to be reattached to a different course. Deleting a module entirely (via DELETE /modules/{uuid}) is only possible once it has been detached from all courses.

Summary: prefer archiving over deleting

ScenarioRecommended action
Course discontinuedPATCH /courses/{uuid}{ "status": "archived" }
Institution inactivePATCH /institutions/{uuid}{ "status": "inactive" }
Student record to be deactivatedDo not delete; leave the record intact
Module no longer taughtDetach from course; do not delete the module
Cohort endedNo action needed; cohorts are historical records