> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agribackup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cryptographic Evidence: Hedera Proofs and HMAC Signatures

> Retrieve and verify Hedera DLT transaction IDs, Merkle state proofs, and HMAC signatures for batch, polygon, and shipment entities.

Every compliance-relevant event in AgriBackup is recorded on the Hedera Hashgraph and returned to you as verifiable cryptographic evidence. This evidence is the foundation of your EUDR audit trail — customs authorities, competent authority inspectors, and your own compliance team can independently verify every record without contacting AgriBackup.

## What cryptographic evidence means in AgriBackup

When you ingest a polygon, register a batch, or link a shipment, AgriBackup submits a transaction to the Hedera Consensus Service (HCS). Hedera returns a transaction ID and a consensus timestamp that are globally and permanently recorded. AgriBackup stores these alongside a Merkle root hash derived from the submitted compliance payload.

The result is that every major compliance event has three independently verifiable properties:

* **Existence:** The Hedera transaction ID proves the event was submitted to the network at a specific time.
* **Integrity:** The Merkle root hash proves the content of the payload has not changed since anchoring.
* **Ordering:** The Hedera consensus timestamp provides a tamper-proof sequence of events across your entire supply chain.

Additionally, all webhook payloads are signed with **HMAC-SHA256** so that your receiving endpoint can verify that the payload originated from AgriBackup and has not been modified in transit.

## The three evidence types

AgriBackup exposes dedicated evidence endpoints for the three entities that carry legal compliance significance:

### Batch evidence

A `LedgerEvidenceResponse` for a batch records the Hedera anchoring that occurred when you registered the commodity batch. It contains the transaction IDs from the initial registration and from any subsequent updates (e.g. risk assessment state changes).

**Endpoint:** `GET /api/v1/enterprise/eudr/evidence/ledger/batch/{batchId}`

> Returns raw Hedera transaction IDs, state proofs, and Merkle root hashes for a consolidated EUDR batch. All anchoring is on the Hedera DLT layer.

### Polygon evidence

A `LedgerEvidenceResponse` for a polygon records the satellite screening result anchored on Hedera, proving that a specific farm boundary was assessed against Copernicus imagery at a specific time.

**Endpoint:** `GET /api/v1/enterprise/eudr/evidence/ledger/polygon/{polygonId}`

> Returns raw Hedera transaction IDs, state proofs, and Merkle root hashes for a verified production polygon.

### Shipment evidence

A `LedgerEvidenceResponse` for a shipment traverses the supply chain graph to locate all EUDR batches mathematically linked to a shipment ID or Bill of Lading, returning their consolidated Hedera state proofs.

**Endpoint:** `GET /api/v1/enterprise/eudr/evidence/ledger/shipment/{shipmentId}`

> Traverses the supply chain graph to locate all EUDR batches mathematically linked to this shipment ID or Bill of Lading, returning their consolidated Hedera state proofs.

## The LedgerEvidenceResponse schema

All three endpoints return the same `LedgerEvidenceResponse` model:

<ResponseField name="entityId" type="string" required>
  The UUID of the entity (batch, polygon, or shipment) that this evidence covers.
</ResponseField>

<ResponseField name="entityType" type="string" required>
  Either `BATCH` or `POLYGON`. For shipment evidence, `entityType` reflects the batch(es) linked to the shipment.
</ResponseField>

<ResponseField name="hederaTransactionIds" type="array of string" required>
  All Hedera consensus transaction IDs associated with this entity across its full lifecycle. Example entry: `"0.0.8713513@1713583200-000000000"`
</ResponseField>

<ResponseField name="merkleRootHash" type="string">
  The Merkle root hash anchored on Hedera. Use this to verify that the compliance payload content has not changed since anchoring. Example: `"0xabc123..."`
</ResponseField>

<ResponseField name="stateProofs" type="array of HederaStateProof" required>
  An array of individual Hedera state proof entries. Each entry captures a single ledger operation.
</ResponseField>

<ResponseField name="consensusTimestamp" type="string">
  The ISO-8601 timestamp of the most recent Hedera consensus event for this entity. Example: `"2026-06-12T12:00:00Z"`
</ResponseField>

### HederaStateProof object

Each entry in the `stateProofs` array describes one discrete ledger operation:

<ResponseField name="hederaTransactionId" type="string" required>
  The Hedera HCS transaction ID for this operation. Format: `{shardId}.{realmId}.{accountId}@{seconds}-{nanoseconds}`. Example: `"0.0.12345@1234567890.000000000"`
</ResponseField>

<ResponseField name="consensusTimestamp" type="string" required>
  The Hedera consensus timestamp for this specific operation. Example: `"2026-06-12T12:00:00"`
</ResponseField>

<ResponseField name="merkleProof" type="string">
  A JSON string encoding the Merkle proof path. Provide this to a Hedera state proof verifier to independently confirm the payload hash.
</ResponseField>

<ResponseField name="operationType" type="string" required>
  The type of ledger operation recorded. Example values: `CREATED`, `UPDATED`, `LINKED`, `REVOKED`.
</ResponseField>

## Retrieving evidence

Use the following code samples to retrieve evidence for each entity type:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import agribackup
    from agribackup.rest import ApiException

    configuration = agribackup.Configuration(host="https://live.agribackup.com/api/v1")
    configuration.api_key['ApiKeyAuth'] = 'sk_live_YOUR_API_KEY'

    with agribackup.ApiClient(configuration) as api_client:
        evidence_api = agribackup.EnterpriseEvidenceApi(api_client)

        try:
            # Retrieve batch evidence
            batch_evidence = evidence_api.get_batch_ledger_evidence("uuid-batch-1234")
            print("Hedera TX IDs:", batch_evidence.hedera_transaction_ids)
            print("Merkle root:", batch_evidence.merkle_root_hash)
            print("Consensus timestamp:", batch_evidence.consensus_timestamp)

            # Retrieve polygon evidence
            polygon_evidence = evidence_api.get_polygon_ledger_evidence("uuid-poly-5678")
            for proof in polygon_evidence.state_proofs:
                print(f"  {proof.operation_type} at {proof.consensus_timestamp}")
                print(f"  TX: {proof.hedera_transaction_id}")

            # Retrieve shipment evidence (traverses all linked batches)
            shipment_evidence = evidence_api.get_shipment_ledger_evidence("BOL-99281744")
            print("Shipment evidence entity:", shipment_evidence.entity_id)

        except ApiException as e:
            print("Exception:", e)
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    import { AgriBackupClient } from '@agribackup/sdk';

    const client = new AgriBackupClient({
      apiKey: 'sk_live_YOUR_API_KEY',
      baseUrl: 'https://live.agribackup.com/api/v1'
    });

    async function retrieveEvidence() {
      try {
        // Retrieve batch evidence
        const batchEvidence = await client.evidence.getBatchLedgerEvidence('uuid-batch-1234');
        console.log('Hedera TX IDs:', batchEvidence.hederaTransactionIds);
        console.log('Merkle root:', batchEvidence.merkleRootHash);
        console.log('Consensus timestamp:', batchEvidence.consensusTimestamp);

        // Retrieve polygon evidence
        const polyEvidence = await client.evidence.getPolygonLedgerEvidence('uuid-poly-5678');
        for (const proof of polyEvidence.stateProofs) {
          console.log(`  ${proof.operationType} at ${proof.consensusTimestamp}`);
          console.log(`  TX: ${proof.hederaTransactionId}`);
        }

        // Retrieve shipment evidence
        const shipmentEvidence = await client.evidence.getShipmentLedgerEvidence('BOL-99281744');
        console.log('Shipment evidence entity:', shipmentEvidence.entityId);

      } catch (error) {
        console.error('Error:', error.message);
      }
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.agribackup.client.*;
    import com.agribackup.client.api.*;
    import com.agribackup.client.model.*;

    public class EvidenceRetrieval {
        public static void main(String[] args) {
            ApiClient defaultClient = Configuration.getDefaultApiClient();
            defaultClient.setBasePath("https://live.agribackup.com/api/v1");
            defaultClient.setApiKey("sk_live_YOUR_API_KEY");

            EnterpriseEvidenceApi evidenceApi = new EnterpriseEvidenceApi(defaultClient);

            try {
                // Retrieve batch evidence
                LedgerEvidenceResponse batchEvidence =
                    evidenceApi.getBatchLedgerEvidence("uuid-batch-1234");
                System.out.println("Hedera TX IDs: " + batchEvidence.getHederaTransactionIds());
                System.out.println("Merkle root: " + batchEvidence.getMerkleRootHash());

                // Retrieve polygon evidence
                LedgerEvidenceResponse polyEvidence =
                    evidenceApi.getPolygonLedgerEvidence("uuid-poly-5678");
                for (HederaStateProof proof : polyEvidence.getStateProofs()) {
                    System.out.println("  " + proof.getOperationType()
                        + " at " + proof.getConsensusTimestamp());
                    System.out.println("  TX: " + proof.getHederaTransactionId());
                }

                // Retrieve shipment evidence
                LedgerEvidenceResponse shipmentEvidence =
                    evidenceApi.getShipmentLedgerEvidence("BOL-99281744");
                System.out.println("Shipment entity: " + shipmentEvidence.getEntityId());

            } catch (ApiException e) {
                System.err.println("Exception: " + e.getResponseBody());
            }
        }
    }
    ```
  </Tab>
</Tabs>

## Verifying evidence independently

Because AgriBackup anchors on the public Hedera Hashgraph, anyone with the transaction ID can verify the record without access to AgriBackup:

1. **Retrieve the transaction ID** from `hederaTransactionIds` in the evidence response.
2. **Query the Hedera Mirror Node** at `https://mainnet-public.mirrornode.hedera.com/api/v1/transactions/{transactionId}` for production, or the equivalent testnet mirror node for sandbox.
3. **Compare the Merkle root hash** from the mirror node response against `merkleRootHash` in the AgriBackup evidence response.
4. **Confirm the consensus timestamp** matches `consensusTimestamp` in the evidence response.

A match on all three values proves that the compliance payload recorded in AgriBackup was anchored on the public ledger at the stated time and has not been altered.

<Note>
  The `merkleProof` field in each `HederaStateProof` entry is a JSON-encoded Merkle proof path. This path allows a verifier to reconstruct the Merkle root from the leaf node (the compliance payload hash) without needing the full dataset — standard practice for large-scale supply chain audits.
</Note>

## Bulk archival for auditors

For regulatory inspections or large-scale audits, use the bulk archival API to export a complete evidence bundle:

<Steps>
  <Step title="Trigger the archive job">
    Call `POST /api/v1/enterprise/eudr/reports/archive` to start an asynchronous compilation job. The job assembles TRACES NT XML payloads, Hedera state proofs, and legal document hash records into a single ZIP artifact.

    The endpoint returns `202 Accepted` with a `jobId`. You receive a `report.ready` webhook when compilation completes.
  </Step>

  <Step title="Download the ZIP">
    Call `GET /api/v1/enterprise/eudr/reports/archive/{reportId}/download` to stream the cryptographic ZIP to your storage system.

    The ZIP contains one folder per batch, each holding the DDS XML, the `LedgerEvidenceResponse` JSON, and all referenced land tenure document hashes.
  </Step>

  <Step title="Transmit to competent authorities">
    Hand the ZIP directly to the inspecting authority or upload it to your national competent authority's portal. Every file inside references a public Hedera transaction ID that the authority can independently verify.
  </Step>
</Steps>

<Tip>
  Schedule a monthly archival job and store the resulting ZIP in cold storage to maintain the five-year document retention required by Article 9 of the EUDR.
</Tip>

## HMAC signatures on webhooks

In addition to Hedera-based evidence, AgriBackup uses **HMAC-SHA256** to sign every webhook delivery. The signature in the `X-AgriBackup-Signature` header gives you cryptographic proof that the webhook payload originated from AgriBackup and was not tampered with in transit.

Rotate your webhook signing secret at any time with `POST /api/v1/enterprise/eudr/webhooks/{webhookId}/rotate-secret`. AgriBackup returns the new secret and begins signing subsequent deliveries with it immediately.

See [Async Architecture — Verifying webhook payloads](/concepts/async-architecture#verifying-webhook-payloads) for the verification code examples.
