Skip to main content
A snapshot is Tally’s core record type. Every time you write identity data for a subject — whether for the first time or as an update — you are creating a snapshot. Snapshots are immutable, sequentially versioned, and cryptographically chained so that you always have a tamper-evident audit trail of how a subject’s identity has evolved over time.

What is a snapshot?

A snapshot is a complete, self-contained capture of a subject’s identity state at a specific point in time. It is expressed as an Entity State Envelope v1 — a JSON document with a defined set of required and optional fields described in this page. Every conforming envelope includes these required top-level fields:
FieldTypeDescription
envelope_version"entity_state_envelope_v1"Identifies the envelope schema version
snapshot_idUUID stringGlobally unique identifier for this snapshot
snapshot_versioninteger ≥ 1Sequential version number per subject
generated_atRFC 3339 datetimeWhen the identity state was materialized
subjectobjectsubject_type + subject_id identifying the subject
attributesobjectThe full materialized identity state
evidencearrayEvidence records backing the attributes
auditobjectWho created this snapshot and when
Here is a complete, minimal snapshot for an entity subject:
{
  "envelope_version": "entity_state_envelope_v1",
  "snapshot_id": "3f0b2c2c-2e46-4b58-8c45-3b5c58f4e9b2",
  "snapshot_version": 1,
  "generated_at": "2026-02-18T09:00:00Z",
  "subject": {
    "subject_type": "entity",
    "subject_id": "ent_acme_001"
  },
  "attributes": {
    "legal_name": "Acme Industrial Supply, Inc.",
    "entity_status": "active",
    "formation_jurisdiction_code": "US-DE"
  },
  "evidence": [],
  "audit": {
    "created_by": "kyc_service",
    "created_at": "2026-02-18T09:00:00Z",
    "source": "tally_ingest",
    "request_id": "req_abc123"
  }
}
The attributes field is a free-form JSON object — it accepts any shape you provide. Tally validates required envelope structure but does not constrain your attribute vocabulary. See Subjects for the canonical attribute schemas Tally recommends for interoperability.

Snapshot versioning

Snapshots are numbered sequentially per subject, starting at 1. Each version is immutable — you never edit or delete a snapshot. Instead, when a subject’s identity state changes, you create a new snapshot with an incremented snapshot_version.
{
  "envelope_version": "entity_state_envelope_v1",
  "snapshot_id": "3f0b2c2c-2e46-4b58-8c45-3b5c58f4e9b2",
  "snapshot_version": 1,
  "generated_at": "2026-02-18T09:00:00Z",
  "subject": {
    "subject_type": "entity",
    "subject_id": "ent_acme_001"
  },
  "attributes": {
    "legal_name": "Acme Industrial Supply, Inc.",
    "entity_status": "active",
    "formation_jurisdiction_code": "US-DE"
  },
  "evidence": [],
  "audit": {
    "created_by": "kyc_service",
    "created_at": "2026-02-18T09:00:00Z",
    "source": "tally_ingest"
  }
}
Never reuse a snapshot_id. Each snapshot must have a UUID that is unique across all snapshots in the platform. If you submit a snapshot with a duplicate snapshot_id, Tally will return a 409 Conflict error.

Evidence

Evidence records provide the documentary basis for the attributes in a snapshot. Each entry in the evidence array describes a source document or data artifact that supports one or more attribute values. Required evidence fields:
FieldTypeDescription
evidence_idstringA stable, unique ID for this evidence record within the snapshot
evidence_typestringA producer-defined type label, e.g., "government_registry_extract"
sourcestringHuman-readable name of the data source
captured_atRFC 3339 datetimeWhen the source data was collected
Optional evidence fields:
FieldTypeDescription
retrieved_atRFC 3339 datetimeWhen the artifact was fetched from the source
hashobjectContent hash — { "alg": "SHA-256", "value": "<hex>" }
locatorstringURI or object key pointing to the stored artifact
notesstringFree-text annotation
Here is the evidence array from the real entity.snapshot.json example:
{
  "evidence": [
    {
      "evidence_id": "ev_2026_0001",
      "evidence_type": "government_registry_extract",
      "source": "Delaware Division of Corporations",
      "captured_at": "2026-02-18T16:10:00Z",
      "retrieved_at": "2026-02-18T16:12:00Z",
      "locator": "registry://de/corp/2288117",
      "hash": {
        "alg": "SHA-256",
        "value": "b3c1f1f0d9c06a8b3c2b9f4a2d5c1e9a0b5e1f5c8a7d9e0f1a2b3c4d5e6f7a8b"
      }
    },
    {
      "evidence_id": "ev_2026_0002",
      "evidence_type": "beneficial_ownership_attestation",
      "source": "Acme Compliance Portal",
      "captured_at": "2026-02-19T09:30:00Z",
      "notes": "Attested by compliance officer"
    }
  ]
}
If an evidence record includes a locator, the target resource must be independently access-controlled. Possession of the locator string does not grant access to the underlying artifact.

Attribute paths

The optional attribute_paths field creates fine-grained attribution: it maps individual JSON Pointer paths within attributes to one or more evidence records. This lets you trace exactly which document sourced each field in the snapshot.
{
  "attribute_paths": {
    "/attributes/legal_name": [
      {
        "evidence_id": "ev_2026_0001",
        "evidence_type": "government_registry_extract",
        "role": "primary"
      }
    ],
    "/attributes/registered_address/country_code": [
      {
        "evidence_id": "ev_2026_0001",
        "evidence_type": "government_registry_extract"
      }
    ],
    "/attributes/relationships/0/ownership_percent": [
      {
        "evidence_id": "ev_2026_0002",
        "evidence_type": "beneficial_ownership_attestation",
        "role": "primary"
      }
    ]
  }
}
Each evidence reference in attribute_paths can carry a role:
  • "primary" — the principal source for this attribute
  • "corroborating" — a secondary source that confirms the primary
  • "conflicting" — a source that disagrees with the primary (flag for review)
Paths absent from attribute_paths simply mean “no attribution available for this field” — you should not emit empty placeholder entries.

Diffs

When submitting snapshot v2 or later, you can include a diff object that explicitly records what changed relative to the previous version. Diffs use RFC 6902 JSON Patch format and target paths under /attributes.
{
  "diff": {
    "format": "rfc6902",
    "ops": [
      {
        "op": "test",
        "path": "/attributes/relationships/0/ownership_percent",
        "value": 35
      },
      {
        "op": "replace",
        "path": "/attributes/relationships/0/ownership_percent",
        "value": 40
      },
      {
        "op": "add",
        "path": "/attributes/relationships/0/last_reviewed",
        "value": "2026-02-20"
      }
    ]
  }
}
This example (from relationship-change.snapshot.json) records that a beneficial owner’s stake changed from 35% to 40%, and that the relationship was reviewed on 2026-02-20. Allowed diff operations are add, remove, replace, and test. The move and copy operations are not permitted. The attributes object is always the authoritative state — the diff is a derived convenience and does not affect how Tally stores or hashes the snapshot.
Including a diff is optional but recommended for v2+ snapshots. It makes audit reviews and downstream change-detection significantly easier, and the API’s diff endpoints can use the embedded diff to return faster responses.

Reading snapshots

Tally provides four read patterns for snapshots:

Latest snapshot

GET /v1/subjects/{subject_type}/{subject_id}/snapshots/latestReturns the highest-versioned snapshot for the subject. Add ?verify=hash or ?verify=chain to include integrity metadata.

By version

GET /v1/subjects/{subject_type}/{subject_id}/snapshots/{snapshot_version}Returns the exact snapshot at a given version number.

By ID

GET /v1/snapshots/{snapshot_id}Retrieves a snapshot by its UUID regardless of subject. Useful when you already have a snapshot_id from a grant or webhook payload.

Full history

GET /v1/subjects/{subject_type}/{subject_id}/snapshotsReturns a paginated list of all snapshots for the subject, ordered by snapshot_version descending by default.
All read endpoints accept a view parameter: view=full (default) returns the complete envelope JSON; view=header returns just the snapshot_id, snapshot_version, subject, and generated_at fields without the full attributes payload. For full endpoint reference, see API Reference → Snapshots.