= digest()
:description: Returns the cryptographic digest (hash) of an ECM document object (std.GetDocumentDigest). Used for integrity verification and duplicate detection.
:keywords: digest, GetDocumentDigest, hash, integrity, duplicate detection, document
:page-tags: api, dms

Returns the digest of a document object via `std.GetDocumentDigest`.

By default (`calculate=False`) the digest stored in the database is returned.
When `calculate=True`, the server calculates a fresh digest from the actual
document files and returns both the stored and the calculated digest, allowing
integrity verification.

== Signature

[tabs]
====
Sync::
+
[source,python]
----
ecm.dms.digest(
    model: ECMDocumentModel | int,
    object_type_id: int | None = None,
    *,
    calculate: bool = False,
) -> ECMDocumentDigest
----

Async::
+
[source,python]
----
await ecm.dms.digest(
    model: ECMDocumentModel | int,
    object_type_id: int | None = None,
    *,
    calculate: bool = False,
) -> ECMDocumentDigest
----
====

== Parameters

[cols="1,1,1,3"]
|===
|Parameter |Type |Default |Description

|`model`
|`ECMDocumentModel \| int`
|—
|Either an `ECMDocumentModel` instance (its `id` is used) or a plain integer document object ID.

|`object_type_id`
|`int \| None`
|`None`
|The numeric object type ID. If `None`, the type is resolved automatically via `get_object_type_by_id()`. Providing it avoids an extra round trip.

|`calculate`
|`bool`
|`False`
|When `True`, the server calculates the digest from the actual document files and also returns `local_digest`. When `False`, only the stored database digest is returned.
|===

== Return value

An `ECMDocumentDigest` instance.

=== ECMDocumentDigest fields

[cols="1,1,3"]
|===
|Field |Type |Description

|`table_digest`
|`str`
|The digest value stored in the database. Always present.

|`local_digest`
|`str \| None`
|The freshly calculated digest from the actual files. Only set when `calculate=True`, `None` otherwise.
|===

== Exceptions

[cols="1,3"]
|===
|Exception |Condition

|`ValueError`
|`model` is an `ECMDocumentModel` instance with `id` set to `None`.

|`ECMNotFoundException`
|`object_type_id` is `None` and no object with the given ID exists on the server.
|===

== Examples

=== Read the stored digest

[tabs]
====
Sync::
+
[source,python]
----
doc = ecm.dms.select(InvoiceDocument).where(InvoiceDocument.Title == "Invoice").execute()[0]
result = ecm.dms.digest(doc)
print(result.table_digest)
----

Async::
+
[source,python]
----
doc = (await ecm.dms.select(InvoiceDocument).where(InvoiceDocument.Title == "Invoice").execute())[0]
result = await ecm.dms.digest(doc)
print(result.table_digest)
----
====

=== Verify integrity by comparing stored vs. calculated digest

[tabs]
====
Sync::
+
[source,python]
----
result = ecm.dms.digest(doc, calculate=True)
if result.table_digest == result.local_digest:
    print("Integrity OK")
else:
    print(f"Mismatch! stored={result.table_digest} calculated={result.local_digest}")
----

Async::
+
[source,python]
----
result = await ecm.dms.digest(doc, calculate=True)
if result.table_digest == result.local_digest:
    print("Integrity OK")
else:
    print(f"Mismatch! stored={result.table_digest} calculated={result.local_digest}")
----
====

=== Use with a plain object ID

[tabs]
====
Sync::
+
[source,python]
----
# Provide object_type_id to avoid an extra server round trip
result = ecm.dms.digest(12345, object_type_id=327685)
print(result.table_digest)
----

Async::
+
[source,python]
----
result = await ecm.dms.digest(12345, object_type_id=327685)
print(result.table_digest)
----
====

== See also

* xref:api/dms/files.adoc[files()] — Download the files of a document
* xref:api/dms/get_object_type_by_id.adoc[get_object_type_by_id()] — Resolve the type ID of an object
