Transfers

Create and track custody transfers with hash-chain integrity verification.

Overview

Transfers represent custody transfer events in the chain of custody. Each transfer:

  • Records a custody event (PICKUP, CHECKPOINT, HANDOFF, DELIVERY)
  • Links to a credential for authorization
  • Maintains hash-chain integrity
  • Tracks transfer sessions

Transfer types follow a logical sequence: PICKUP → CHECKPOINT(s) → HANDOFF(s) → DELIVERY.

Methods

create()

Create a custody transfer event.

vinstrrequired

17-character Vehicle Identification Number (excludes I, O, Q)

transfer_typeTransferTyperequired

Type of transfer: PICKUP, CHECKPOINT, HANDOFF, or DELIVERY

credential_idstrrequired

ID of the credential authorizing this transfer

location_idstrrequired

Location ID where transfer occurs (format: LOC-{TYPE}-{ID})

timestampdatetime

When the transfer occurred (default: server time)

metadatadict

Additional metadata for the transfer (optional)

Returns: Transfer object with transfer_id, session_id, hashes, etc.

Example:

from vecu_custody import CustodyClient, TransferType

client = CustodyClient.sandbox(token="your-jwt-token")

# Create initial pickup
transfer = client.transfers.create(
    vin="9HGBH41JXMN999999",
    transfer_type=TransferType.PICKUP,
    credential_id="CRED-12345678",
    location_id="LOC-AUCTION-MANHEIM-ATLANTA",
    metadata={"notes": "Vehicle in good condition"}
)

print(f"Transfer ID: {transfer.transfer_id}")
print(f"Session ID: {transfer.session_id}")
print(f"Event hash: {transfer.event_hash}")

# Later, create delivery
delivery = client.transfers.create(
    vin="9HGBH41JXMN999999",
    transfer_type=TransferType.DELIVERY,
    credential_id="CRED-87654321",
    location_id="LOC-DEALERSHIP-CARMAX-ORLANDO"
)

get_status()

Get current custody status for a VIN.

vinstrrequired

17-character Vehicle Identification Number

Returns: CustodyStatus object with current custodian, location, etc.

Example:

from vecu_custody import CustodyClient

client = CustodyClient.sandbox(token="your-jwt-token")

status = client.transfers.get_status("9HGBH41JXMN999999")

if status.current_custodian:
    print(f"Current custodian: {status.current_custodian}")
    print(f"Location: {status.current_location}")
    print(f"Total transfers: {status.transfer_count}")
else:
    print("No custody record for this VIN")

get_history()

Get transfer history for a VIN with pagination support.

vinstrrequired

17-character Vehicle Identification Number

limitint

Number of items per page (default: 50, max: 100)

next_tokenstr

Pagination cursor from previous response

auto_paginatebool

If True, returns iterator; if False, returns single page. Default: True

Returns: Iterator of Transfer objects (if auto_paginate=True) or TransferHistoryResponse (if auto_paginate=False)

Example:

from vecu_custody import CustodyClient

client = CustodyClient.sandbox(token="your-jwt-token")

# Auto-paginate through all transfers
for transfer in client.transfers.get_history("9HGBH41JXMN999999"):
    print(f"{transfer.transfer_type} at {transfer.created_at}")
    print(f"  Location: {transfer.location_id}")
    print(f"  Hash: {transfer.event_hash}")

# Manual pagination
page = client.transfers.get_history(
    "9HGBH41JXMN999999",
    limit=20,
    auto_paginate=False
)
print(f"Total transfers: {page.total_count}")
if page.next_token:
    next_page = client.transfers.get_history(
        "9HGBH41JXMN999999",
        next_token=page.next_token,
        auto_paginate=False
    )

verify_integrity()

Verify hash-chain integrity for a VIN.

vinstrrequired

17-character Vehicle Identification Number

Returns: IntegrityVerification object indicating if chain is valid

Example:

from vecu_custody import CustodyClient

client = CustodyClient.sandbox(token="your-jwt-token")

verification = client.transfers.verify_integrity("9HGBH41JXMN999999")

if verification.chain_valid:
    print(f"Chain valid: {verification.total_events} events verified")
else:
    print(f"Chain integrity error: {verification.error_message}")

Response Objects

Transfer

transfer_idstr

Unique identifier for the transfer (e.g., "TRF-12345678")

session_idstr

Session ID linking related transfers

vinstr

Vehicle Identification Number

transfer_typeTransferType

Type: PICKUP, CHECKPOINT, HANDOFF, or DELIVERY

statusstr

Transfer status (e.g., "COMPLETED")

credential_idstr

Credential ID authorizing the transfer

location_idstr

Location where transfer occurred

event_hashstr

SHA-256 hash of this transfer event

previous_hashstr

Hash of the previous transfer in the chain

metadatadict

Custom metadata (if provided)

created_atstr

ISO 8601 timestamp when transfer was created

CustodyStatus

vinstr

Vehicle Identification Number

session_idstr

Current session ID

statusstr

Custody status (e.g., "IN_CUSTODY")

current_custodianstr

Current custodian credential ID

current_locationstr

Current location ID

last_transfer_atstr

ISO 8601 timestamp of last transfer

transfer_countint

Total number of transfers for this VIN

IntegrityVerification

vinstr

Vehicle Identification Number

chain_validbool

Whether the custody chain is valid

total_eventsint

Number of events verified

error_messagestr

Error message if chain is invalid (null if valid)

Common Use Cases

Complete Transfer Flow

from vecu_custody import CustodyClient, TransferType

client = CustodyClient.sandbox(token="your-jwt-token")

vin = "9HGBH41JXMN999999"

# Step 1: Pickup at origin
pickup = client.transfers.create(
    vin=vin,
    transfer_type=TransferType.PICKUP,
    credential_id="CRED-DRIVER-123",
    location_id="LOC-AUCTION-MANHEIM-ATLANTA"
)
print(f"Pickup transfer: {pickup.transfer_id}")

# Step 2: Checkpoint during transport
checkpoint = client.transfers.create(
    vin=vin,
    transfer_type=TransferType.CHECKPOINT,
    credential_id="CRED-DRIVER-123",
    location_id="LOC-TRUCK-STOP-I75"
)

# Step 3: Delivery at destination
delivery = client.transfers.create(
    vin=vin,
    transfer_type=TransferType.DELIVERY,
    credential_id="CRED-RECEIVER-456",
    location_id="LOC-DEALERSHIP-CARMAX-ORLANDO"
)
print(f"Delivery transfer: {delivery.transfer_id}")

# Verify integrity
verification = client.transfers.verify_integrity(vin)
print(f"Chain valid: {verification.chain_valid}")

View Transfer History

# Get complete history
history = list(client.transfers.get_history("9HGBH41JXMN999999"))

print(f"Transfer Chain for VIN: 9HGBH41JXMN999999")
print(f"Total transfers: {len(history)}\n")

for i, transfer in enumerate(history, 1):
    print(f"{i}. {transfer.transfer_type}")
    print(f"   Location: {transfer.location_id}")
    print(f"   Date: {transfer.created_at}")
    print(f"   Hash: {transfer.event_hash[:16]}...")
    print()

Check Current Status

status = client.transfers.get_status("9HGBH41JXMN999999")

print(f"Current Status:")
print(f"  Custodian: {status.current_custodian}")
print(f"  Location: {status.current_location}")
print(f"  Last transfer: {status.last_transfer_at}")
print(f"  Total transfers: {status.transfer_count}")

Error Handling

from vecu_custody import CustodyClient, TransferType
from vecu_custody.exceptions import (
    InvalidTransferSequenceError,
    SessionAlreadyClosedError,
    ValidationError
)

client = CustodyClient.sandbox(token="your-jwt-token")

try:
    transfer = client.transfers.create(
        vin="9HGBH41JXMN999999",
        transfer_type=TransferType.DELIVERY,
        credential_id="CRED-12345678",
        location_id="LOC-DEALERSHIP-CARMAX-ORLANDO"
    )
except InvalidTransferSequenceError as e:
    print(f"Invalid sequence: {e.message}")
    print("You must create a PICKUP before DELIVERY")
except SessionAlreadyClosedError as e:
    print(f"Session closed: {e.session_id}")
except ValidationError as e:
    print(f"Invalid parameters: {e.message}")

Next Steps