Soft Delete & Restore
Deleting a record is non-destructive by default: the row is marked deleted (deletedAt/deletedBy stamped) and disappears from normal queries, but remains recoverable. This protects against accidental deletion, supports a trash/undo workflow, and keeps an audit trail. Permanent (hard) delete is a separate, permission-gated operation for cases that demand irreversible erasure.
| Method & Path | Description |
|---|---|
DELETE /api/tables/:tableId/records/:recordId |
Soft delete a record |
DELETE /api/tables/:tableId/records/:recordId?permanent=true |
Permanently delete a record |
GET /api/tables/:tableId/trash |
Browse soft-deleted records |
POST /api/tables/:tableId/records/:recordId/restore |
Restore a soft-deleted record |
POST /api/tables/:tableId/records/batch/restore |
Restore many records |
Soft delete
DELETE /api/tables/orders/records/123
This sets deletedAt to the current time and deletedBy to the acting user, then excludes the row from default list and read responses. The operation is logged to the record's change history.
Permanent (hard) delete
DELETE /api/tables/orders/records/123?permanent=true
Hard delete removes the row irreversibly and requires the permanentDelete permission. When force-delete is not allowed for a table, the endpoint returns 404 (anti-enumeration) rather than 403.
Hard-delete is opt-in and admin-gated. Reserve ?permanent=true for genuine erasure needs — e.g. GDPR right-to-erasure on personal data. Financial ledgers and audit-adjacent tables should rely on soft delete only. Permanent delete is an irreversible operation: there is no restore.
Cascade behavior on related records
When a record is deleted, related records are handled per the relationship field's onDelete policy. Configure it on the relational field.
tables:
- id: 1
name: orders
permissions:
delete: ['admin', 'member']
permanentDelete: ['admin'] # Permanent delete requires admin role
fields:
- id: 1
name: customer_id
type: relationship
relatedTable: customers
onDelete: cascade # cascade | set-null | restrict
onDelete |
Effect when the parent is deleted |
|---|---|
cascade |
Delete the dependent (child) records too |
set-null |
Clear the foreign-key reference on dependents |
restrict |
Block the delete while dependents exist |
Trash view
GET /api/tables/:tableId/trash lists soft-deleted records so a UI can present a recoverable bin. The same result is available on the list endpoint via includeDeleted=only.
Restore
POST /api/tables/orders/records/123/restore
Restore clears deletedAt (returning the row to normal queries), stamps a restored_at, captures the restoring user, and logs the operation to history.
{
"id": 123,
"deleted_at": null,
"restored_at": "2025-01-15T11:00:00Z"
}
| Status | Meaning |
|---|---|
200 OK |
Record restored |
400 Bad Request |
Record is not currently deleted |
401 Unauthorized |
No active session |
404 Not Found |
Record absent or not visible |
Batch restore
Recover many records in one transaction. Records that are not currently deleted are skipped; a missing id rolls the whole batch back (404). See Batch Operations.
POST /api/tables/orders/records/batch/restore
{
"ids": ["123", "124", "125"]
}
Querying deleted records
By default, soft-deleted rows are excluded from list and read responses. Override with the includeDeleted query parameter:
| Value | Behavior |
|---|---|
| (omitted) | Active records only (default) |
true |
Active and deleted records |
only |
Soft-deleted records only (equivalent to the trash view) |
GET /api/tables/orders/records?includeDeleted=true
GET /api/tables/orders/records?includeDeleted=only
Permissions summary
| Operation | Permission |
|---|---|
| Soft delete | delete (per table permissions + RBAC) |
| Permanent delete | permanentDelete (typically admin only) |
| Restore | delete / restore grant for the role |
Related pages
- Records Overview —
deletedBy/deletedAtauthorship - CRUD & Upsert — the single-record delete endpoint
- Batch Operations — batch delete and restore
- Relational Fields —
onDeletecascade configuration - Record History & Comments — deletes and restores are logged