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

# Function Management

> Update, version, monitor, and manage your deployed Functions

<Tip>
  STOP. Do not read past this section until you have read and followed [/quickstart.md](/quickstart.md).

  Goal: produce a reliable SDK automation script. The intended authoring flow is generate then edit, not write browser automation from scratch.

  Do not hand-write SDK code with element IDs, selectors, or form fields from memory. Element IDs, selectors, and field mappings must come from a live `observe()` call, CLI `notte page observe` output, or generated workflow code. Guessed browser targets fail on real pages.

  Use the Notte skill and CLI to inspect the live site, validate actions/selectors, handle auth/session state, and export workflow code with `notte sessions workflow-code` before using SDK docs or SDK code.

  SDK reference pages are for understanding, running, or editing generated workflow code. They are not the starting point for manually authoring the initial browser automation. SDK-first code is guesswork on real-world pages with dynamic selectors, auth state, CAPTCHAs, and anti-bot behavior.
</Tip>

Manage the lifecycle of your Functions including updates, versioning, monitoring executions, and viewing replays.

## Viewing Functions

### List All Functions

View all deployed Functions:

{/* @sniptest testers/functions/list_functions.py */}

```python list_functions.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# List all functions
functions = client.functions.list()

for func in functions.items:
    print(f"ID: {func.workflow_id}")
    print(f"Name: {func.name}")
    print(f"Version: {func.latest_version}")
    print(f"Created: {func.created_at}")
    print("---")
```

### Get Function Details

Retrieve specific Function information:

{/* @sniptest testers/functions/management/get_function_details.py */}

```python get_function_details.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Get function by ID
function = client.Function(function_id="func_abc123")

# Access function properties
print(f"Function ID: {function.function_id}")
print(f"Name: {function.response.name}")
print(f"Description: {function.response.description}")
print(f"Latest Version: {function.response.latest_version}")
print(f"Versions: {function.response.versions}")
```

## Updating Functions

### Update Function Code

Deploy a new version of your Function:

{/* @sniptest testers/functions/management/update_code.py */}

```python update_code.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Update with new code
function.update(path="updated_function.py")

print(f"Updated to version: {function.response.latest_version}")
```

### Update Function Metadata

Change Function name or description:

```python theme={null}
# Update metadata (via console or API)
# Name and description updates are available through the Console
```

**Note:** Function updates create new versions automatically. Previous versions remain accessible for rollback.

## Versioning

### Version Management

Functions automatically version on each update:

{/* @sniptest testers/functions/management/version_management.py */}

```python version_management.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Get all versions
print(f"Available versions: {function.response.versions}")

# Get latest version
print(f"Latest: {function.response.latest_version}")
```

### Run Specific Version

Execute a specific Function version:

{/* @sniptest testers/functions/management/run_specific_version.py */}

```python run_specific_version.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Run latest version (default)
result = function.run(url="https://example.com")

# Run specific version
result = function.run(url="https://example.com", version="v2")
```

### Version History

Track changes across versions:

{/* @sniptest testers/functions/management/version_history.py */}

```python version_history.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

functions = client.functions.list()

for func in functions.items:
    print(f"Function: {func.name}")
    print(f"Versions: {', '.join(func.versions)}")
    print(f"Latest: {func.latest_version}")
```

## Monitoring Executions

### View Run History

List recent Function executions:

{/* @sniptest testers/functions/management/view_run_history.py */}

```python view_run_history.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Get recent runs
runs = client.functions.list_runs(
    function_id="function_abc123",
    only_active=False,  # Include completed runs
)

for run in runs.items:
    print(f"Run ID: {run.workflow_run_id}")
    print(f"Status: {run.status}")
    print(f"Created: {run.created_at}")
    print(f"Updated: {run.updated_at}")
    print("---")
```

### Check Run Status

Monitor specific execution:

{/* @sniptest testers/functions/management/check_run_status.py */}

```python check_run_status.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Get run details
run_status = client.functions.get_run("function_abc123", "run_xyz789")

print(f"Status: {run_status.status}")  # "active", "closed", "failed"
print(f"Result: {run_status.result}")
print(f"Session ID: {run_status.session_id}")
```

**Status values:**

* `active` - Function is currently running
* `closed` - Function completed successfully
* `failed` - Function encountered an error

### Filter Active Runs

View only running executions:

{/* @sniptest testers/functions/management/filter_active_runs.py */}

```python filter_active_runs.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Get only active runs
active_runs = client.functions.list_runs("function_abc123", only_active=True)

print(f"Active runs: {len(active_runs.items)}")

for run in active_runs.items:
    print(f"Run {run.workflow_run_id} - {run.status}")
```

## Viewing Replays

### Access Function Replay

Watch MP4 replay of Function execution:

{/* @sniptest testers/functions/management/access_replay.py */}

```python access_replay.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Run the function first
result = function.run(url="https://example.com")

# Get replay for this run (uses the session from the last run)
replay = function.replay()

# Download the replay
replay.download("function_replay.mp4")
```

**Replay features:**

* Video recording of browser session
* Shows all actions taken
* Useful for debugging failures
* Available after running a function

### Replay After Run

Get replay immediately after execution:

{/* @sniptest testers/functions/management/replay_after_run.py */}

```python replay_after_run.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Run function
result = function.run(url="https://example.com")

# Get replay for this run (automatically uses the session from the run)
replay = function.replay()

# Download the video
replay.download("execution_replay.mp4")
```

## Managing Runs

### Stop Running Function

Cancel a long-running execution:

{/* @sniptest testers/functions/management/stop_running.py */}

```python stop_running.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Stop specific run
client.functions.stop_run("function_abc123", "run_xyz789")

print("Function execution stopped")
```

### Monitoring Long Runs

Track execution progress:

{/* @sniptest testers/functions/monitor_runs.py */}

```python monitor_runs.py theme={null}
import time

from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Start run
result = function.run(
    url="https://example.com",
    stream=False,  # Don't stream logs
)

run_id = result.workflow_run_id

# Poll status
while True:
    status = client.functions.get_run("func_abc123", run_id)

    print(f"Status: {status.status}")

    if status.status in ["closed", "failed"]:
        print(f"Final result: {status.result}")
        break

    time.sleep(5)  # Check every 5 seconds
```

## Forking Functions

### Fork Shared Function

Create your own copy of a shared Function:

{/* @sniptest testers/functions/management/fork_function.py */}

```python fork_function.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Fork a function from marketplace or teammate
original = client.Function(function_id="func_abc123")
forked_function = original.fork()

print(f"Forked function ID: {forked_function.function_id}")
print("Original ID: func_abc123")
```

**Use cases:**

* Customize shared Functions
* Create templates from marketplace
* Copy teammate's Functions
* Experiment without affecting original

### Modify Forked Function

After forking, update as needed:

{/* @sniptest testers/functions/management/modify_forked.py */}

```python modify_forked.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Fork function
original = client.Function(function_id="shared_function_id")
forked = original.fork()

# Update your copy
forked.update(path="my_modified_version.py")

# Run your version
result = forked.run(url="https://example.com")
```

## Downloading Functions

### Download Function Code

Retrieve Function source code:

{/* @sniptest testers/functions/management/download_code.py */}

```python download_code.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(
    function_id="func_abc123",
    decryption_key="your-decryption-key",  # Required for downloading
)

# Download function code
code = function.download()

print(code)  # Function source code

# Or download directly to a file
code = function.download(path="downloaded_function.py")
```

**Requirements:**

* Decryption key (available in Console) - passed when creating Function instance
* Only works for Functions you own or have access to

### Download for Backup

Back up all your Functions:

{/* @sniptest testers/functions/management/backup_functions.py */}

```python backup_functions.py theme={null}
import os

from notte_sdk import NotteClient

client = NotteClient()

functions = client.functions.list()

# Create backup directory
os.makedirs("function_backups", exist_ok=True)

for func in functions.items:
    function = client.Function(function_id=func.workflow_id, decryption_key="your-key")

    try:
        code = function.download()

        filename = f"function_backups/{func.name}_{func.latest_version}.py"
        with open(filename, "w") as f:
            f.write(code)

        print(f"Backed up: {func.name}")
    except Exception as e:
        print(f"Failed to backup {func.name}: {e}")
```

## Deleting Functions

### Delete Function

Remove a Function permanently:

{/* @sniptest testers/functions/management/delete_function.py */}

```python delete_function.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Delete function
function.delete()

print("Function deleted")
```

**Warning:**

* Deletion is permanent
* All versions are deleted
* Run history is preserved
* Cannot be undone

## Sharing Functions

### Make Function Public

Share Functions with others:

{/* @sniptest testers/functions/management/make_public.py */}

```python make_public.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Deploy as shared
function = client.Function(
    path="my_function.py",
    name="Public Scraper",
    shared=True,  # Make publicly accessible
)
```

**Shared Functions:**

* ✅ Others can view and fork
* ✅ Original remains under your control
* ❌ Others cannot modify your version
* ✅ Usage tracked to your account

### Private Functions

Keep Functions private (default):

{/* @sniptest testers/functions/management/private_function.py */}

```python private_function.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(
    path="my_function.py",
    name="Private Automation",
    shared=False,  # Private (default)
)
```

## Best Practices

### 1. Version Your Functions

Update incrementally with clear changes:

{/* @sniptest testers/functions/management/version_backup.py */}

```python version_backup.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

# Before major update, download current version
current_code = function.download(decryption_key="key")

# Save backup
with open(f"backups/function_v{function.response.latest_version}.py", "w") as f:
    f.write(current_code)

# Update function
function.update(path="new_version.py")
```

### 2. Monitor Function Health

Regular health checks:

{/* @sniptest testers/functions/function_health_check.py */}

```python function_health_check.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()


def check_function_health(function_id: str):
    """Check recent run success rate."""
    runs = client.functions.list_runs(function_id, only_active=False)

    total = len(runs.items)
    failed = sum(1 for r in runs.items if r.status == "failed")

    success_rate = ((total - failed) / total * 100) if total > 0 else 0

    print(f"Success rate: {success_rate:.1f}%")
    print(f"Total runs: {total}")
    print(f"Failed runs: {failed}")

    return success_rate


# Monitor function
check_function_health("func_abc123")
```

### 3. Clean Up Old Runs

Archive or analyze old execution data:

{/* @sniptest testers/functions/management/cleanup_old_runs.py */}

```python cleanup_old_runs.py theme={null}
import json
from datetime import datetime, timedelta

from notte_sdk import NotteClient

client = NotteClient()


def archive_old_runs(function_id: str, days_old: int = 30):
    """Archive runs older than specified days."""
    runs = client.functions.list_runs(function_id, only_active=False)

    cutoff_date = datetime.now() - timedelta(days=days_old)
    archive = []

    for run in runs.items:
        if run.created_at < cutoff_date:
            archive.append(
                {
                    "run_id": run.workflow_run_id,
                    "status": run.status,
                    "created_at": run.created_at.isoformat(),
                    "result": str(run.result)[:100],  # First 100 chars
                }
            )

    # Save archive
    with open(f"archives/{function_id}_{datetime.now().date()}.json", "w") as f:
        json.dump(archive, f, indent=2)

    print(f"Archived {len(archive)} old runs")


archive_old_runs("function_abc123")
```

### 4. Document Changes

Keep change log for Function updates:

{/* @sniptest testers/functions/management/document_changes.py */}

```python document_changes.py theme={null}
"""
Function: Data Scraper
Function ID: func_abc123

Version History:
- v1 (2024-01-15): Initial release
- v2 (2024-01-20): Added error handling
- v3 (2024-01-25): Improved scraping accuracy
- v4 (2024-02-01): Added retry logic
"""


def run(url: str):
    # Your function code
    pass
```

### 5. Test Before Production

Validate updates before deploying:

{/* @sniptest testers/functions/management/test_before_prod.py */}

```python test_before_prod.py theme={null}
from notte_sdk import NotteClient


def validate_before_production():
    client = NotteClient()

    # Test locally first - decryption key needed for local execution
    function = client.Function(function_id="func_abc123", decryption_key="your-key")

    # Run with local=True for testing
    test_result = function.run(url="https://test-site.com", local=True)

    if test_result.status == "closed":
        print("Test passed, ready to update production")
        function.update(path="tested_function.py")
    else:
        print(f"Test failed: {test_result.result}")
```

## Troubleshooting

### Function Fails to Run

Check run details for errors:

{/* @sniptest testers/functions/management/troubleshoot_fails.py */}

```python troubleshoot_fails.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

function = client.Function(function_id="func_abc123")

run = client.functions.get_run("func_abc123", "run_xyz789")

if run.status == "failed":
    print(f"Error: {run.result}")

    # Run the function to get a replay
    result = function.run()
    replay = function.replay()
    replay.download("debug_replay.mp4")
```

### Missing Environment Variables

Verify configuration:

{/* @sniptest testers/functions/management/missing_env_vars.py */}

```python missing_env_vars.py theme={null}
import os


def run():
    required_vars = ["API_KEY", "WEBHOOK_URL"]

    missing = [var for var in required_vars if not os.getenv(var)]

    if missing:
        return {"error": f"Missing environment variables: {', '.join(missing)}"}

    # Continue with automation
```

### High Failure Rate

Analyze failure patterns:

{/* @sniptest testers/functions/management/high_failure_rate.py */}

```python high_failure_rate.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

runs = client.functions.list_runs("function_abc123")

failures = [r for r in runs.items if r.status == "failed"]

print(f"Failed runs: {len(failures)}/{len(runs.items)}")

# Analyze failure reasons
for run in failures[:5]:  # Last 5 failures
    print(f"Run {run.workflow_run_id}:")
    print(f"  Error: {run.result}")
    print(f"  Time: {run.created_at}")
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Creating Functions" icon="file-code" href="/features/functions/creating">
    Learn how to write Functions
  </Card>

  <Card title="Invocations" icon="play" href="/features/functions/invocations">
    Call Functions via API or SDK
  </Card>

  <Card title="Schedules" icon="clock" href="/features/functions/schedules">
    Schedule Functions with cron
  </Card>

  <Card title="Functions Concept" icon="lightbulb" href="/concepts/functions">
    Back to Functions overview
  </Card>
</CardGroup>
