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

# Document Metadata in the SDK

> Work with document-level metadata and labels in Kodexa, storing properties such as title, author, dates, and processing status separately from node features.

Document metadata stores key-value properties that describe the document itself, such as title, author, creation date, or processing status. This is distinct from content node features which are attached to specific nodes in the document tree.

## Setting Metadata

### Individual Values

<CodeGroup>
  ```python Python theme={null}
  from kodexa_document import Document

  with Document() as doc:
      # Set individual metadata values
      doc.set_metadata("title", "Invoice #12345")
      doc.set_metadata("author", "Accounting System")
      doc.set_metadata("created_date", "2024-01-15")
      doc.set_metadata("document_type", "invoice")

      # Values can be strings, numbers, booleans, lists, or dicts
      doc.set_metadata("page_count", 3)
      doc.set_metadata("processed", True)
      doc.set_metadata("tags", ["financial", "q1-2024", "priority"])
      doc.set_metadata("source", {
          "system": "ERP",
          "id": "DOC-2024-001"
      })
  ```

  ```typescript TypeScript theme={null}
  import { Kodexa } from '@kodexa-ai/document-wasm-ts';

  async function setMetadata() {
    await Kodexa.init();
    const doc = await Kodexa.createDocument();

    try {
      // Set individual metadata values
      await doc.setMetadataValue('title', 'Invoice #12345');
      await doc.setMetadataValue('author', 'Accounting System');
      await doc.setMetadataValue('createdDate', '2024-01-15');
      await doc.setMetadataValue('documentType', 'invoice');

      // Values can be strings, numbers, booleans, arrays, or objects
      await doc.setMetadataValue('pageCount', 3);
      await doc.setMetadataValue('processed', true);
      await doc.setMetadataValue('tags', ['financial', 'q1-2024', 'priority']);
      await doc.setMetadataValue('source', {
        system: 'ERP',
        id: 'DOC-2024-001'
      });
    } finally {
      doc.dispose();
    }
  }
  ```
</CodeGroup>

### Bulk Metadata

<CodeGroup>
  ```python Python theme={null}
  with Document(metadata={
      "title": "Invoice #12345",
      "author": "Accounting System",
      "created_date": "2024-01-15"
  }) as doc:
      # Metadata is set at creation time
      print(f"Title: {doc.get_metadata('title')}")

  # Or replace all metadata at once
  with Document() as doc:
      doc.metadata = {
          "title": "Updated Title",
          "version": "2.0"
      }
  ```

  ```typescript TypeScript theme={null}
  async function setBulkMetadata() {
    await Kodexa.init();
    const doc = await Kodexa.createDocument();

    try {
      // Set multiple values at once
      await doc.setMetadata({
        title: 'Invoice #12345',
        author: 'Accounting System',
        createdDate: '2024-01-15'
      });
    } finally {
      doc.dispose();
    }
  }
  ```
</CodeGroup>

## Reading Metadata

### Individual Values

<CodeGroup>
  ```python Python theme={null}
  with Document.from_kddb("document.kddb") as doc:
      # Get specific metadata values
      title = doc.get_metadata("title")
      author = doc.get_metadata("author")

      print(f"Title: {title}")
      print(f"Author: {author}")

      # Non-existent keys return None
      missing = doc.get_metadata("nonexistent")
      print(f"Missing: {missing}")  # None
  ```

  ```typescript TypeScript theme={null}
  async function readMetadata() {
    await Kodexa.init();
    const doc = await Kodexa.fromBlob(kddbBlob);

    try {
      // Get specific metadata values
      const title = await doc.getMetadataValue('title');
      const author = await doc.getMetadataValue('author');

      console.log(`Title: ${title}`);
      console.log(`Author: ${author}`);

      // Non-existent keys return null/undefined
      const missing = await doc.getMetadataValue('nonexistent');
      console.log(`Missing: ${missing}`);
    } finally {
      doc.dispose();
    }
  }
  ```
</CodeGroup>

### All Metadata

<CodeGroup>
  ```python Python theme={null}
  with Document.from_kddb("document.kddb") as doc:
      # Access all metadata as a dict-like object
      metadata = doc.metadata

      # Iterate through all metadata
      for key in metadata:
          print(f"{key}: {metadata[key]}")

      # Check if key exists
      if "title" in metadata:
          print(f"Has title: {metadata['title']}")
  ```

  ```typescript TypeScript theme={null}
  async function readAllMetadata() {
    await Kodexa.init();
    const doc = await Kodexa.fromBlob(kddbBlob);

    try {
      // Get all metadata as an object
      const metadata = await doc.getMetadata();

      // Iterate through all metadata
      for (const [key, value] of Object.entries(metadata)) {
        console.log(`${key}: ${value}`);
      }

      // Check if key exists
      if (metadata.title) {
        console.log(`Has title: ${metadata.title}`);
      }
    } finally {
      doc.dispose();
    }
  }
  ```
</CodeGroup>

## Document Labels

Labels provide a simple way to categorize documents with string tags:

<CodeGroup>
  ```python Python theme={null}
  with Document() as doc:
      # Add labels
      doc.add_label("invoice")
      doc.add_label("financial")
      doc.add_label("q1-2024")
      doc.add_label("processed")

      # Get all labels
      labels = doc.labels
      print(f"Labels: {labels}")  # ['invoice', 'financial', 'q1-2024', 'processed']

      # Check for specific label
      if "invoice" in doc.labels:
          print("This is an invoice")
  ```

  ```typescript TypeScript theme={null}
  async function workWithLabels() {
    await Kodexa.init();
    const doc = await Kodexa.createDocument();

    try {
      // Labels are typically stored in metadata
      await doc.setMetadataValue('labels', ['invoice', 'financial', 'q1-2024']);

      // Read labels
      const labels = await doc.getMetadataValue('labels') || [];
      console.log(`Labels: ${labels}`);

      // Check for specific label
      if (labels.includes('invoice')) {
        console.log('This is an invoice');
      }
    } finally {
      doc.dispose();
    }
  }
  ```
</CodeGroup>

## Document Identity

Every document has a unique identifier and version:

<CodeGroup>
  ```python Python theme={null}
  with Document() as doc:
      # UUID is auto-generated
      print(f"Document UUID: {doc.uuid}")

      # Version tracks document changes
      print(f"Version: {doc.version}")
  ```

  ```typescript TypeScript theme={null}
  async function getDocumentIdentity() {
    await Kodexa.init();
    const doc = await Kodexa.createDocument();

    try {
      // UUID is auto-generated
      const metadata = await doc.getMetadata();
      console.log(`Document UUID: ${metadata.uuid}`);
    } finally {
      doc.dispose();
    }
  }
  ```
</CodeGroup>

## Source Metadata

Track the original source of the document:

<CodeGroup>
  ```python Python theme={null}
  with Document(source={
      "connector": "file-upload",
      "original_filename": "invoice_scan.pdf",
      "mime_type": "application/pdf",
      "size": 125000
  }) as doc:
      # Access source information
      source = doc.source
      print(f"Original file: {source.original_filename}")
      print(f"MIME type: {source.mime_type}")
  ```

  ```typescript TypeScript theme={null}
  async function trackSource() {
    await Kodexa.init();
    const doc = await Kodexa.createDocument();

    try {
      // Store source information in metadata
      await doc.setMetadataValue('source', {
        connector: 'file-upload',
        originalFilename: 'invoice_scan.pdf',
        mimeType: 'application/pdf',
        size: 125000
      });

      const source = await doc.getMetadataValue('source');
      console.log(`Original file: ${source.originalFilename}`);
    } finally {
      doc.dispose();
    }
  }
  ```
</CodeGroup>

## Common Metadata Patterns

### Document Classification

<CodeGroup>
  ```python Python theme={null}
  with Document() as doc:
      doc.set_metadata("document_type", "invoice")
      doc.set_metadata("confidence", 0.95)
      doc.set_metadata("classification_model", "doc-classifier-v2")
      doc.set_metadata("classification_timestamp", "2024-01-15T10:30:00Z")

      # Alternative classifications
      doc.set_metadata("classifications", [
          {"type": "invoice", "confidence": 0.95},
          {"type": "receipt", "confidence": 0.03},
          {"type": "contract", "confidence": 0.02}
      ])
  ```

  ```typescript TypeScript theme={null}
  async function classifyDocument(doc: KddbDocument, predictions: Array<{type: string, confidence: number}>) {
    const topPrediction = predictions[0];

    await doc.setMetadataValue('documentType', topPrediction.type);
    await doc.setMetadataValue('confidence', topPrediction.confidence);
    await doc.setMetadataValue('classificationModel', 'doc-classifier-v2');
    await doc.setMetadataValue('classificationTimestamp', new Date().toISOString());
    await doc.setMetadataValue('classifications', predictions);
  }
  ```
</CodeGroup>

### Processing Status

<CodeGroup>
  ```python Python theme={null}
  with Document() as doc:
      # Track processing status
      doc.set_metadata("status", "pending")
      doc.set_metadata("created_at", "2024-01-15T10:00:00Z")

      # After processing
      doc.set_metadata("status", "complete")
      doc.set_metadata("processed_at", "2024-01-15T10:30:00Z")
      doc.set_metadata("processing_time_ms", 1500)
      doc.set_metadata("processor_version", "1.2.3")
  ```

  ```typescript TypeScript theme={null}
  async function trackProcessingStatus(doc: KddbDocument) {
    // Initial status
    await doc.setMetadataValue('status', 'pending');
    await doc.setMetadataValue('createdAt', new Date().toISOString());

    // After processing
    const startTime = Date.now();
    // ... processing ...
    const endTime = Date.now();

    await doc.setMetadataValue('status', 'complete');
    await doc.setMetadataValue('processedAt', new Date().toISOString());
    await doc.setMetadataValue('processingTimeMs', endTime - startTime);
    await doc.setMetadataValue('processorVersion', '1.2.3');
  }
  ```
</CodeGroup>

### Audit Trail

<CodeGroup>
  ```python Python theme={null}
  with Document() as doc:
      doc.set_metadata("created_by", "user@example.com")
      doc.set_metadata("created_at", "2024-01-15T10:00:00Z")
      doc.set_metadata("modified_by", "admin@example.com")
      doc.set_metadata("modified_at", "2024-01-15T14:30:00Z")
      doc.set_metadata("modification_history", [
          {
              "user": "user@example.com",
              "action": "created",
              "timestamp": "2024-01-15T10:00:00Z"
          },
          {
              "user": "admin@example.com",
              "action": "approved",
              "timestamp": "2024-01-15T14:30:00Z"
          }
      ])
  ```

  ```typescript TypeScript theme={null}
  async function addAuditEntry(doc: KddbDocument, user: string, action: string) {
    // Get existing history
    const history = (await doc.getMetadataValue('modificationHistory')) || [];

    // Add new entry
    history.push({
      user: user,
      action: action,
      timestamp: new Date().toISOString()
    });

    // Update metadata
    await doc.setMetadataValue('modifiedBy', user);
    await doc.setMetadataValue('modifiedAt', new Date().toISOString());
    await doc.setMetadataValue('modificationHistory', history);
  }
  ```
</CodeGroup>

## Metadata vs Node Features

| Aspect   | Metadata                              | Node Features                  |
| -------- | ------------------------------------- | ------------------------------ |
| Scope    | Document-level                        | Per-node                       |
| Purpose  | Document properties                   | Node-specific data             |
| Examples | Title, author, status                 | Font, position, OCR confidence |
| Access   | `doc.metadata` / `doc.get_metadata()` | `node.get_feature()`           |
| Querying | Not queryable via selectors           | Queryable via selectors        |
