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

# Artifacts

> Persistent key-value storage for AI-generated content

Artifacts in Open WebUI provide a built-in key-value storage API for persistent data. This enables AI models to create and maintain long-lived content such as journals, trackers, leaderboards, collaborative tools, and interactive applications that persist across chat sessions.

## Overview

Artifacts enable:

* Persistent key-value data storage
* Personal and shared data scopes
* JSON-based structured data
* Cross-session persistence
* Collaborative features
* Interactive AI-powered applications

<Note>
  Artifacts are built directly into Open WebUI, providing a simple storage API that AI models can use to create stateful applications without external databases.
</Note>

## What Are Artifacts?

Artifacts are persistent data objects that survive beyond individual chat conversations. Unlike memories which store context and preferences, artifacts store structured application data.

Common use cases:

* **Journals**: Daily logs and note-taking
* **Trackers**: Habit tracking, goal monitoring
* **Leaderboards**: Competitive rankings and scores
* **Dashboards**: Data visualization and metrics
* **Collaborative Docs**: Shared documents and wikis
* **Games**: Persistent game state

## Data Scopes

Artifacts support two data scopes:

### Personal Scope

Data accessible only to the individual user:

```javascript theme={null}
// Store personal data
const personalData = {
  key: "my-journal",
  scope: "personal",
  value: {
    entries: [
      {
        date: "2024-03-02",
        content: "Today I learned about artifacts..."
      }
    ]
  }
};
```

### Shared Scope

Data accessible to all users (workspace-wide):

```javascript theme={null}
// Store shared data
const sharedData = {
  key: "team-leaderboard",
  scope: "shared",
  value: {
    scores: [
      { user: "Alice", points: 150 },
      { user: "Bob", points: 120 }
    ]
  }
};
```

## Storage Operations

### Store Data

Save or update an artifact:

```python theme={null}
import requests
import json

url = "http://localhost:8080/api/artifacts/store"
payload = {
    "key": "habit-tracker",
    "scope": "personal",
    "value": {
        "habits": [
            {"name": "Exercise", "completed": ["2024-03-01", "2024-03-02"]},
            {"name": "Reading", "completed": ["2024-03-01"]}
        ]
    }
}

response = requests.post(url, json=payload)
result = response.json()

if result["success"]:
    print("Artifact stored successfully")
```

### Retrieve Data

Get an artifact by key:

```python theme={null}
import requests

url = "http://localhost:8080/api/artifacts/get"
params = {
    "key": "habit-tracker",
    "scope": "personal"
}

response = requests.get(url, params=params)
artifact = response.json()

if artifact:
    print(f"Habits: {artifact['value']['habits']}")
else:
    print("Artifact not found")
```

### List Artifacts

Get all artifacts for a scope:

```python theme={null}
import requests

# List personal artifacts
response = requests.get(
    "http://localhost:8080/api/artifacts/list",
    params={"scope": "personal"}
)
personal_artifacts = response.json()

for artifact in personal_artifacts:
    print(f"Key: {artifact['key']}")
    print(f"Updated: {artifact['updated_at']}")
```

### Delete Artifact

Remove an artifact:

```python theme={null}
import requests

url = "http://localhost:8080/api/artifacts/delete"
params = {
    "key": "old-tracker",
    "scope": "personal"
}

response = requests.delete(url, params=params)

if response.json()["success"]:
    print("Artifact deleted")
```

## Example Applications

### Daily Journal

<Accordion title="Journal Application">
  ```python theme={null}
  import requests
  from datetime import datetime

  class JournalApp:
      def __init__(self):
          self.base_url = "http://localhost:8080/api/artifacts"
          self.key = "daily-journal"
      
      def add_entry(self, content):
          """Add a new journal entry."""
          # Get existing journal
          response = requests.get(
              f"{self.base_url}/get",
              params={"key": self.key, "scope": "personal"}
          )
          
          journal = response.json() or {"entries": []}
          
          # Add new entry
          entry = {
              "date": datetime.now().isoformat(),
              "content": content
          }
          journal["entries"].append(entry)
          
          # Save updated journal
          requests.post(
              f"{self.base_url}/store",
              json={
                  "key": self.key,
                  "scope": "personal",
                  "value": journal
              }
          )
      
      def get_recent_entries(self, count=5):
          """Get recent journal entries."""
          response = requests.get(
              f"{self.base_url}/get",
              params={"key": self.key, "scope": "personal"}
          )
          
          journal = response.json()
          if journal:
              entries = journal["entries"][-count:]
              return entries
          return []

  # Usage
  journal = JournalApp()
  journal.add_entry("Today I learned about Open WebUI artifacts!")
  recent = journal.get_recent_entries(3)
  ```
</Accordion>

### Habit Tracker

<Accordion title="Habit Tracker Application">
  ```python theme={null}
  import requests
  from datetime import datetime, date

  class HabitTracker:
      def __init__(self):
          self.base_url = "http://localhost:8080/api/artifacts"
          self.key = "habit-tracker"
      
      def add_habit(self, habit_name):
          """Create a new habit to track."""
          tracker = self._load_tracker()
          
          if habit_name not in tracker["habits"]:
              tracker["habits"][habit_name] = {
                  "created": datetime.now().isoformat(),
                  "completed_dates": []
              }
              self._save_tracker(tracker)
      
      def mark_complete(self, habit_name, date_str=None):
          """Mark a habit as completed for today or specific date."""
          if date_str is None:
              date_str = date.today().isoformat()
          
          tracker = self._load_tracker()
          
          if habit_name in tracker["habits"]:
              completed = tracker["habits"][habit_name]["completed_dates"]
              if date_str not in completed:
                  completed.append(date_str)
                  self._save_tracker(tracker)
      
      def get_streak(self, habit_name):
          """Calculate current streak for a habit."""
          tracker = self._load_tracker()
          
          if habit_name not in tracker["habits"]:
              return 0
          
          completed = sorted(
              tracker["habits"][habit_name]["completed_dates"],
              reverse=True
          )
          
          streak = 0
          current_date = date.today()
          
          for completed_date in completed:
              if completed_date == current_date.isoformat():
                  streak += 1
                  current_date = current_date - timedelta(days=1)
              else:
                  break
          
          return streak
      
      def _load_tracker(self):
          response = requests.get(
              f"{self.base_url}/get",
              params={"key": self.key, "scope": "personal"}
          )
          return response.json() or {"habits": {}}
      
      def _save_tracker(self, tracker):
          requests.post(
              f"{self.base_url}/store",
              json={
                  "key": self.key,
                  "scope": "personal",
                  "value": tracker
              }
          )

  # Usage
  tracker = HabitTracker()
  tracker.add_habit("Exercise")
  tracker.mark_complete("Exercise")
  print(f"Current streak: {tracker.get_streak('Exercise')} days")
  ```
</Accordion>

### Team Leaderboard

<Accordion title="Leaderboard Application">
  ```python theme={null}
  import requests
  from typing import List, Dict

  class Leaderboard:
      def __init__(self, board_name="default"):
          self.base_url = "http://localhost:8080/api/artifacts"
          self.key = f"leaderboard-{board_name}"
      
      def add_score(self, user_name: str, points: int):
          """Add or update a user's score."""
          board = self._load_board()
          
          # Find existing user
          user_entry = next(
              (u for u in board["scores"] if u["name"] == user_name),
              None
          )
          
          if user_entry:
              user_entry["points"] += points
          else:
              board["scores"].append({
                  "name": user_name,
                  "points": points
              })
          
          # Sort by points (descending)
          board["scores"].sort(key=lambda x: x["points"], reverse=True)
          
          self._save_board(board)
      
      def get_top(self, count: int = 10) -> List[Dict]:
          """Get top N players."""
          board = self._load_board()
          return board["scores"][:count]
      
      def get_rank(self, user_name: str) -> int:
          """Get a user's rank (1-based)."""
          board = self._load_board()
          
          for i, entry in enumerate(board["scores"], start=1):
              if entry["name"] == user_name:
                  return i
          
          return -1  # Not found
      
      def _load_board(self):
          response = requests.get(
              f"{self.base_url}/get",
              params={"key": self.key, "scope": "shared"}
          )
          return response.json() or {"scores": []}
      
      def _save_board(self, board):
          requests.post(
              f"{self.base_url}/store",
              json={
                  "key": self.key,
                  "scope": "shared",
                  "value": board
              }
          )

  # Usage
  board = Leaderboard("weekly-challenge")
  board.add_score("Alice", 50)
  board.add_score("Bob", 30)
  board.add_score("Alice", 20)  # Alice now has 70 total

  top_players = board.get_top(5)
  for i, player in enumerate(top_players, start=1):
      print(f"{i}. {player['name']}: {player['points']} points")
  ```
</Accordion>

### Interactive Dashboard

<Accordion title="Dashboard Application">
  ```python theme={null}
  import requests
  from datetime import datetime, timedelta

  class MetricsDashboard:
      def __init__(self):
          self.base_url = "http://localhost:8080/api/artifacts"
          self.key = "metrics-dashboard"
      
      def log_metric(self, metric_name: str, value: float):
          """Log a metric value with timestamp."""
          dashboard = self._load_dashboard()
          
          if metric_name not in dashboard["metrics"]:
              dashboard["metrics"][metric_name] = []
          
          dashboard["metrics"][metric_name].append({
              "timestamp": datetime.now().isoformat(),
              "value": value
          })
          
          # Keep only last 100 entries per metric
          dashboard["metrics"][metric_name] = \
              dashboard["metrics"][metric_name][-100:]
          
          self._save_dashboard(dashboard)
      
      def get_latest(self, metric_name: str):
          """Get the most recent value for a metric."""
          dashboard = self._load_dashboard()
          
          if metric_name in dashboard["metrics"]:
              entries = dashboard["metrics"][metric_name]
              if entries:
                  return entries[-1]["value"]
          
          return None
      
      def get_average(self, metric_name: str, hours: int = 24):
          """Calculate average over the last N hours."""
          dashboard = self._load_dashboard()
          
          if metric_name not in dashboard["metrics"]:
              return None
          
          cutoff = datetime.now() - timedelta(hours=hours)
          entries = dashboard["metrics"][metric_name]
          
          recent = [
              e["value"] for e in entries
              if datetime.fromisoformat(e["timestamp"]) > cutoff
          ]
          
          return sum(recent) / len(recent) if recent else None
      
      def _load_dashboard(self):
          response = requests.get(
              f"{self.base_url}/get",
              params={"key": self.key, "scope": "shared"}
          )
          return response.json() or {"metrics": {}}
      
      def _save_dashboard(self, dashboard):
          requests.post(
              f"{self.base_url}/store",
              json={
                  "key": self.key,
                  "scope": "shared",
                  "value": dashboard
              }
          )

  # Usage
  dashboard = MetricsDashboard()
  dashboard.log_metric("cpu_usage", 45.2)
  dashboard.log_metric("memory_usage", 62.8)

  print(f"Current CPU: {dashboard.get_latest('cpu_usage')}%")
  print(f"Avg CPU (24h): {dashboard.get_average('cpu_usage', 24):.1f}%")
  ```
</Accordion>

## Best Practices

### Data Structure

1. **Use JSON**: Store well-structured JSON data
2. **Version Your Schema**: Include version fields for migrations
3. **Keep It Small**: Store only necessary data
4. **Index When Needed**: Use keys that facilitate lookups

```python theme={null}
# Good: Structured, versioned data
artifact = {
    "key": "user-settings",
    "value": {
        "version": "1.0",
        "preferences": {
            "theme": "dark",
            "language": "en"
        },
        "updated_at": "2024-03-02T10:00:00Z"
    }
}
```

### Performance

1. **Cache Locally**: Don't fetch on every operation
2. **Batch Updates**: Combine multiple changes
3. **Limit Size**: Keep artifacts under 1MB
4. **Clean Old Data**: Implement data retention policies

### Security

1. **Validate Input**: Always validate before storing
2. **Sanitize Output**: Clean data before display
3. **Scope Appropriately**: Use personal scope for sensitive data
4. **Don't Store Secrets**: Never store passwords or API keys

## Error Handling

```python theme={null}
import requests

def safe_artifact_operation():
    try:
        response = requests.post(
            "http://localhost:8080/api/artifacts/store",
            json={
                "key": "my-data",
                "scope": "personal",
                "value": {"data": "value"}
            },
            timeout=5
        )
        response.raise_for_status()
        return response.json()
    
    except requests.exceptions.Timeout:
        print("Request timed out")
    except requests.exceptions.HTTPError as e:
        print(f"HTTP error: {e}")
    except Exception as e:
        print(f"Error: {e}")
    
    return None
```

## Troubleshooting

### Artifact Not Found

* Verify the key is correct
* Check you're using the right scope (personal vs shared)
* Ensure the artifact was successfully saved
* Confirm you have permission to access the scope

### Data Not Persisting

* Check the API response for errors
* Verify JSON data is valid
* Ensure sufficient storage space
* Review application logs

### Performance Issues

* Reduce artifact size
* Implement caching
* Limit update frequency
* Archive old data

## Next Steps

* Learn about [Memories](/advanced/memories) for context storage
* Explore [Functions](/advanced/functions) to create artifact-powered tools
* Build [Skills](/advanced/skills) that leverage artifacts
