Erplain does not apply the same deletion behaviour everywhere. This page explains the two cases, how to tell them apart, and how to detect a deletion reliably.
Soft deletion
Most catalogue and contact resources are soft-deleted: the record is retained, but excluded from queries by default. Their queries accept a trashed argument:
| Value | Effect |
|---|---|
ONLY | Return only deleted records. |
WITH | Return both deleted and non-deleted records. |
WITHOUT | Return only non-deleted records (default). |
query DeletedProducts {
Products(trashed: ONLY, first: 50) {
paginatorInfo { total }
data { id label deleted_at }
}
}Resources supporting trashed:
Contacts — Customer, Supplier, Employee, AccountRepresentative
Catalogue — Product, Variant, Attribute, Option, Brand, ProductTag, BatchNumber, CartItem, CustomField, Location, Season, SupplierTag, CustomerTag
Pricing — PriceLevel, DiscountRule
Billing — TaxCode, TaxRate, PaymentInformation, PaymentMethod, PdfTemplate
Accounting — AccountingFamily, AccountingExport
Manufacturing — BillOfMaterial
B2B store — B2bStoreProducts, B2bStoreBrands, B2bStoreSeasons, B2bStoreProductTags, B2bStoreCartItems
For most of these the argument is available on both the single and the paginated query (Customer and Customers, Product and Products, and so on). Two exceptions: AccountingExport is only available as a single-record query, and the B2bStore* queries exist only in their paginated form.
Definitive deletion
Documents are deleted definitively. A deleted document never reappears: no query returns it, and looking it up by id returns null.
Documents without trashed: Invoice, Order, Purchase, PurchaseReceipt, Estimate, CreditNote, Refund, ProductReturn, Subscription, ManufacturingOrder, Payment, SerialNumber, StockAdjustment, StockLevel, PriceRule, Currency, EmailTemplate
This is deliberate and consistent across the whole document family: trashed is available wherever soft deletion exists, and documents do not use it.
Detecting a deletion
For a soft-deletable resource, query with trashed: WITH and compare the id against your own registry.
For a definitively deleted document, there is no direct signal. A single-record query returns null both for a deleted record and for an id that never existed, so a lookup alone cannot tell the two apart. The reliable method is reconciliation against a full read:
- Keep the ids returned by your last synchronisation.
- On each synchronisation, read the resource page by page.
- Any id still in your registry but absent from the complete read has been deleted.
query SyncManufacturingOrders($first: Int, $page: Int) {
ManufacturingOrders(first: $first, page: $page) {
paginatorInfo { total currentPage lastPage }
data {
id
label
status
updated_at
}
}
}Walk page from 1 to lastPage, collect the id values, and diff them against your registry.
Note: Queries are not capped by the mutations-per-request limit, but the overall rate limit still applies (2 requests per second). Read a full resource in as few requests as possible by raising
first, rather than polling it repeatedly.
Identifiers are stable
id values are never recycled. Once an id has been used it will not be assigned to another record, deleted or not, so ids remain safe as keys in your own storage.
What this means for your integration
- Soft-deletable resources: deletions are visible immediately through
trashed. - Documents: deletions are only visible by reconciliation, so build your synchronisation around a complete read rather than a lookup per record.
- A
nullresult from a single-record query is never, on its own, proof of deletion.
