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

# Proof of Transfer

> As a Canton Network explorer, enhance your UpdateId detail view with the Proof of Transfer functionality for DA Registry Assets. As a wallet builder add a unique verification feature to your offering.

<Tip>
  Integrating **Proof of Transfer** capability into a Canton Network Explorer gives users a seamless interface to verify transaction statuses natively, while preserving privacy.
</Tip>

<Tabs>
  <Tab title="Canton Network Explorer">
    ## Integration Blueprint

    To provide a seamless customer journey, the Explorer handles incoming payload information—an `UpdateID` and a `Transfer Object` payload—usually supplied via direct user copy-pasting or automated routing via a platform referral URL parameter. The platform then submits this data payload to the **DA Registry API** to render an indisputable confirmation receipt.

    ## Step-by-Step Implementation Flow

    Follow this workflow sequence to build the verification stack inside your network explorer ecosystem.

    <Steps>
      <Step title="1. Configure the Target Inbound Route">
        Create a dedicated, public route in your application routing profile (e.g., `/verify-transfer`) designated to handle incoming cryptographic verification payloads. Ensure the handler is equipped to extract query string components seamlessly if redirected from external wallet histories.
      </Step>

      <Step title="2. Construct the Payload Input Form Interface">
        Design a clean input card component. If parameters are present in the URL path, auto-populate the data fields for the user. If blank, provide a structured text area box allowing the user to paste their raw transaction JSON payload manually.
      </Step>

      <Step title="3. Query the DA Registry Endpoint">
        Dispatch an asynchronous network request from your application frontend layer directly to the live DA Registry network service infrastructure node using the structured body schemas.

        ```json Curl Request Signature theme={null}
        curl -X POST "https://api.utilities.digitalasset.com/api/utilities/v0/registry/transfer/v0/proof" \
          -H "Content-Type: application/json" \
          -d '{
            "updateId": "1220b1223907e943aa24b52e874a78c86067bafcdb429958994cbd2b33fc41df5e1a",
            "transfer": {
              "sender": "issuer::12204b6be4677e1e110e63cb3dcd89c62025932458547a09e580458e29e822e9ee26",
              "receiver": "holder::12204b6be4677e1e110e63cb3dcd89c62025932458547a09e580458e29e822e9ee26",
              "amount": "1.0000000000",
              "instrumentId": {
                "admin": "registrar::12204b6be4677e1e110e63cb3dcd89c62025932458547a09e580458e29e822e9ee26",
                "id": "INST"
              },
              "requestedAt": "2025-12-09T13:29:42.6187Z",
              "executeBefore": "2025-12-12T13:28:53.7492Z",
              "inputHoldingCids": [
                "0020a2a23208de8d9fec1efd1c377d9a4343eac83f27b26b554f1c9e95c9653c05ca12122056c38daac3b523885fe8436215c72d0181e81e34379918246fc0ef75852990f9"
              ]
            }
          }'
        ```
      </Step>

      <Step title="4. Parse Outcomes & Render Visual Verification Status">
        Read the structured enum value returned from the API service. Update the client view container to reflect the deterministic ledger evaluation output cleanly using descriptive status layouts.
      </Step>
    </Steps>

    ## API Request Schema Specifications

    Your interface layer must accurately frame the request body definitions before transmitting data packages to the network node validation server.

    ### Technical Payload Fields

    <ParamField body="updateId" type="string" required>
      The unique cryptographic ledger hash tracking indicator marking the target execution sequence window.
    </ParamField>

    <ParamField body="transfer" type="object" required>
      The private core transaction details package required to process cryptographic balance sheet verification.

      <Expandable title="Properties Block">
        <ParamField body="sender" type="string" required>
          The specific identity fingerprint address origin string dispatching the digital asset.
        </ParamField>

        <ParamField body="receiver" type="string" required>
          The destination identity fingerprint address authorized to take legal custody of the asset.
        </ParamField>

        <ParamField body="amount" type="string" required>
          Alphanumeric decimal string specifying the absolute transfer token unit volume.
        </ParamField>

        <ParamField body="instrumentId" type="object" required>
          Identifies the core registry blueprint details governing the tokenized asset.
        </ParamField>

        <ParamField body="requestedAt" type="string (date-time)" required>
          ISO-8601 string mapping the exact timestamp historical moment the transfer workflow initialization sequence fired.
        </ParamField>

        <ParamField body="executeBefore" type="string (date-time)" required>
          The drop-dead expiration constraint timeline timestamp bounding the transaction validity lifecycle on-chain.
        </ParamField>

        <ParamField body="inputHoldingCids" type="array of strings" required>
          The explicit array of structural tracking contract identifiers used directly to fund the ledger movement.
        </ParamField>
      </Expandable>
    </ParamField>

    ## UI Component States & Response Handling

    To ensure alignment with network expectations, map the direct string responses from the **DA Registry API** into intuitive, color-coded dashboard indicators for your end users.

    <Tabs>
      <Tab title="Success Resolution">
        ### Rendering UI Target: Balanced Verification Badge

        The transaction variables are authenticated against the ledger state and indicate successful contract finalization.

        ```json Response Sample theme={null}
        {
          "status": "Success"
        }
        ```

        <Note>
          **Interface Suggestion:** Render a prominent checkmark module highlighting an "Indisputably Settled" receipt statement. Users can confidently treat this transaction as settled.
        </Note>
      </Tab>

      <Tab title="Pending Evaluation">
        ### Rendering UI Target: Progress Indicator Badge

        The parameters represent a structurally valid ledger entry path, but the contract steps are still moving through live execution queues.

        ```json Response Sample theme={null}
        {
          "status": "Pending"
        }
        ```

        <Warning>
          **Interface Suggestion:** Inform users that the asset transfer is in an intermediate lifecycle phase (e.g., a two-step transfer workflow where the destination party hasn't formally accepted an open offer yet). Advise re-evaluating once final settlement commits.
        </Warning>
      </Tab>

      <Tab title="Failure Response">
        ### Rendering UI Target: Terminated Execution Badge

        The proof data successfully resolved to a historical ledger event path, but the workflow instructions failed execution or were rejected.

        ```json Response Sample theme={null}
        {
          "status": "Failure"
        }
        ```

        <Warning>
          **Interface Suggestion:** Update the dashboard to inform the user that the transaction request was intentionally canceled, explicitly aborted by a counterparty, or simply expired on-chain.
        </Warning>
      </Tab>

      <Tab title="Validation Error Handling">
        ### Rendering UI Target: Input Mismatch Panel (HTTP 400)

        The input fields failed lookup, are missing parameters, or contain structured mismatches against live on-chain transactional tracking parameters.

        ```json Response Sample theme={null}
        {
          "error": "invalid_request",
          "error_description": "Something went wrong"
        }
        ```

        <Warning>
          **Data Privacy Guardrail Rule:** The backend deliberately zero-discloses tracking details to prevent unwanted transaction state farming by unauthorized actors. Ensure your Explorer frontend handles errors elegantly with a general "Invalid Proof Credentials" module rather than displaying custom data logs.
        </Warning>
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Canton Network Wallet">
    ## Guide overview

    To enable this transfer proofing capability, wallets must make the **Transfer Object** payload and **UpdateID** extractable, allowing end-users to easily locate and copy this data directly from their transaction history to use in proofing services or network explorers.

    This guide provides the technical specifications for locating and extracting the Transfer Object from the ledger via the JSON API.

    ## Exposing the Transfer Object and UpdateID

    ### Technical Guidelines: What Constitutes the Transfer Object?

    The **UpdateID** is the unique identifier for a transaction on the ledger. With it, you can fetch the Created, Exercised, and Archived events for that specific transaction. **NOTE: The wallets need to ensure that they display the most recent update for a given transfer. For example, in the case of a 2 step transfer, Wallet Providers will need to explicitly refresh the updateID, after the transaction has been accepted, so that they display the transfer, not just the offer.**

    The underlying schema defining the Transfer Object can be referenced in the DAML model here: `Splice/Api/Token/TransferInstructionV1.daml`

    The Transfer Object can also be serialized into JSON format, as shown in the following example:

    <Tabs>
      <Tab title="JSON">
        ```json theme={null}
        {
            "sender": "issuer::122...",
            "receiver": "holder::122...",
            "amount": "2.0000000000",
            "instrumentId": { "admin": "...", "id": "INST" },
            "requestedAt": "2026-03-01T13:58:32.626Z",
            "executeBefore": "2026-03-04T13:58:27.335Z",
            "inputHoldingCids": ["005152f0eae9..."],
            "meta": {
                "values": {
                    "splice.lfdecentralizedtrust.org/reason": ""
                }
            }
        }
        ```
      </Tab>
    </Tabs>

    **Fetching the Data via JSON API**

    While gRPC can be used, this guide assumes integration via the [DAML JSON API](/sdks-tools/api-reference/json-api).

    To fetch update info from the participant node, query the following endpoint using the transaction's `UpdateID`: `GET /v2/updates/update-by-id`

    ### Locating the Transfer Object (By Transaction State)

    Depending on the lifecycle stage of the transaction, the Transfer Object is located in different event arguments. You will need to parse the events returned from the endpoint above based on these three scenarios:

    **Scenario A: The Transfer Offer is Created**

    If the update represents the creation of a transfer offer, look for a **Created** event.

    * **Template:** `Utility.Registry.App.V0.Model.Transfer:TransferOffer`
    * **Location:** Extract the Transfer Object directly from the `createArgument` of this event.

    <Tabs>
      <Tab title="JSON">
        ```json theme={null}
        {
          "createdEvent": {
            "templateId": "...:Utility.Registry.App.V0.Model.Transfer:TransferOffer",
            "createArgument": {
              "operator": "operator::122...",
              "provider": "provider::122...",
              "transfer": {
                "sender": "issuer::122...",
                "receiver": "holder::122...",
                "amount": "2.0000000000",
                "instrumentId": { "admin": "...", "id": "INST" },
                "requestedAt": "2026-03-01T13:58:32.626Z",
                "executeBefore": "2026-03-04T13:58:27.335Z",
                "inputHoldingCids": ["005152f0eae9..."],
                "meta": { "values": { "splice.lfdecentralizedtrust.org/reason": "" } }
              }
            }
          }
        }
        ```
      </Tab>
    </Tabs>

    **Scenario B: The Transfer is Concluded**

    If the update represents a concluded transfer (e.g., an accepted offer or a pre-approved transfer), look for an **Exercised** event.

    * **Template:** `Utility.Registry.V0.Rule.Transfer:TransferRule`
    * **Triggering Choices:** The event must be triggered by one of the following choices:
      * `TransferRule_DirectTransfer`
      * `TransferRule_TwoStepTransfer`
      * `TransferRule_Transfer` (This choice will be deprecated. It is only required for backwards compatibility)
    * **Location:** Extract the Transfer Object from the `choiceArgument` of this event.

    <Tabs>
      <Tab title="JSON">
        ```json theme={null}
        {
          "ExercisedEvent": {
            "templateId": "...:Utility.Registry.V0.Rule.Transfer:TransferRule",
            "choice": "TransferRule_DirectTransfer",
            "choiceArgument": {
              "transfer": {
                "sender": "auth0...::122...",
                "receiver": "auth0...::122...",
                "amount": "12.0000000000",
                "instrumentId": { "admin": "...", "id": "INST" },
                "requestedAt": "2026-02-24T16:27:33.139Z",
                "executeBefore": "2026-02-27T16:27:31.633Z",
                "inputHoldingCids": ["0000c37c6..."],
                "meta": { "values": { "splice.lfdecentralizedtrust.org/reason": "" } }
              }
            }
          }
        }
        ```
      </Tab>
    </Tabs>

    **Scenario C: The Transfer Offer is Rejected or Withdrawn**

    If the update represents an offer that was ultimately rejected or withdrawn, locating the object is a two-step process:

    1. **Identify the Event:** Look for an **Exercised** event matching the following:
       * **Interface ID:** `Splice.Api.Token.TransferInstructionV1:TransferInstruction`
       * **Triggering Choices:** `TransferInstruction_Reject` OR `TransferInstruction_Withdraw`
    2. **Fetch the Contract ID:** Extract the Contract ID of the transfer offer from this exercised event.
    3. **Fetch the Original Offer:** Query the JSON API using the extracted Contract ID: `GET /v2/events/events-by-contract-id`
    4. **Location:** Extract the Transfer Object from the `createArgument` of the original transfer offer (transfer instruction) returned by this secondary query.

    <Tabs>
      <Tab title="JSON">
        ```json theme={null}
        {
          "CreatedEvent": {
            "templateId": "...:Utility.Registry.App.V0.Model.Transfer:TransferOffer",
            "createArgument": {
              "operator": "operator::12209b02d...",
              "provider": "provider::1220c07e7...",
              "transfer": {
                "sender": "issuer::1220c07e7...",
                "receiver": "holder::1220c07e7...",
                "amount": "2.0000000000",
                "instrumentId": { "admin": "registrar::1220c07e7...", "id": "INST" },
                "requestedAt": "2026-03-01T13:58:32.626Z",
                "executeBefore": "2026-03-04T13:58:27.335Z",
                "inputHoldingCids": ["005152f0eae9..."],
                "meta": { "values": { "splice.lfdecentralizedtrust.org/reason": "" } }
              }
            }
          }
        }
        ```
      </Tab>
    </Tabs>

    ### Data Persistence and Pruning

    **Ledger data is subject to pruning.** The JSON API queries described in the previous section will only succeed if the transaction events have not yet been pruned from the participant node.

    **Wallet Developer Action Required:** To ensure end-users can always access their Transfer Objects for the proofing service, **wallets MUST persist the Transfer Object and UpdateID data in their own backend databases** at the time the transaction occurs. Relying strictly on real-time ledger queries for historical transactions will result in errors once the transactions are pruned. PQS can be considered as an option for storing ledger data.

    ### Locating the Transfer Object (as an end-user)

    **Labelling**

    There are two key items to be displayed within the transaction details:

    1. UpdateID
    2. Transfer object

    **Displaying value**

    A truncated preview of the UpdateID can be displayed or in full. A truncated preview of the json object can be displayed as needed or none at all.

    **Interactive options**

    Users must be able to review the full json object.

    Option 1

    * Click to open a modal/side-panel component or an external browser window. Since the content is hidden to start, when a user clicks to open this component, the json object should by default be displayed fully. This component shows the label of "Transfer object", contains the full json object for reviewing, and an accessible copy button.
    * If an external window is used, its domain must match the application domain from which the window was triggered.
    * The icon or button for a user to click to review the json object must be accessible, alongside the copy icon/button. Users can copy the object without opening the review component.

    Option 2

    * Click to open the accordion containing the content. The accordion is closed by default, and a copy button is accessible without opening the accordion.

    <Note>
      The Proof of Transfer API is supporting the validation of multiple transactions within an UpdateID, that means also Batch Transfer can be validated. In order to enable it from a wallet perspective, ensure that end users can extract the `Transfer Object` for each individual transaction of a batch.
    </Note>

    **Copying behavior**

    The copy behavior must always copy the full UpdateID and full json object, never the truncated preview string.
  </Tab>
</Tabs>
