Skip to main content
The Disk API provides persistent, S3-backed storage for file artifacts in your Acontext projects. You can organize files using a filesystem-like structure with paths, store custom metadata, and generate secure download URLs.

What you’ll build

In this quickstart, you’ll learn how to:
  • Create a disk for storing files
  • Upload files with custom metadata
  • Retrieve files with presigned URLs
  • Organize files using paths
  • Update metadata and manage artifacts

Prerequisites

Before you begin, ensure you have:
  • A running Acontext server (run locally)
  • An Acontext API key (default is sk-ac-your-root-api-bearer-token)

Initialize the client

First, create a client instance with your API key and base URL.
from acontext import AcontextClient

client = AcontextClient(
    api_key="sk-ac-your-root-api-bearer-token",
    base_url="http://localhost:8029/api/v1"
)
client.ping()
Never hardcode API keys in production code. Use environment variables instead.

Step-by-step tutorial

1

Create a disk

Create a new disk to store your artifacts. Each disk is an isolated storage container with a unique ID.
# Create a new disk
disk = client.disks.create()
print(f"Created disk: {disk.id}")
The disk object contains:
  • id: Unique disk identifier
  • project_id: Your project ID
  • created_at: ISO 8601 timestamp
  • updated_at: ISO 8601 timestamp
Save the disk ID - you’ll need it to upload and retrieve files.
2

Upload a file

Upload your first file to the disk. You can specify a path to organize files and add custom metadata.
from acontext import FileUpload

# Create a file upload
file = FileUpload(
    filename="sprint-plan.md",
    content=b"# Sprint Plan\n\n## Goals\n- Complete user authentication\n- Fix critical bugs"
)

# Upload to disk with path and metadata
artifact = client.disks.artifacts.upsert(
    disk.id,
    file=file,
    file_path="/documents/2024/",
    meta={
        "department": "engineering",
        "author": "alice",
        "version": "1.0"
    }
)

print(f"Uploaded: {artifact.filename}")
print(f"Path: {artifact.path}")
The upsert method creates a new artifact or updates an existing one if a file with the same path and filename already exists.
Use meaningful paths like /images/, /documents/, or /reports/2024/ to organize your files logically.
3

Retrieve a file with URL

Get an artifact and generate a presigned URL for secure downloading. You can also retrieve the file content directly.
# Get artifact with public URL and content
result = client.disks.artifacts.get(
    disk.id,
    file_path="/documents/2024/",
    filename="sprint-plan.md",
    with_public_url=True,
    with_content=True
)

print(f"Public URL: {result.public_url}")
print(f"Metadata: {result.artifact.meta}")

# Access file content if available
if result.content:
    print(f"Content type: {result.content.type}")
    print(f"Content: {result.content.raw[:100]}...")  # First 100 chars
The response includes:
  • artifact: Full artifact object with metadata
  • public_url: Presigned URL for downloading (if requested)
  • content: Parsed file content for text, JSON, CSV, or code files (if requested)
Presigned URLs are temporary and expire after a default duration. You can customize expiration using the expire parameter (in seconds).
4

List artifacts in a path

View all files and subdirectories at a specific path.
# List artifacts in the documents folder
result = client.disks.artifacts.list(
    disk.id,
    path="/documents/2024/"
)

print("Files:")
for artifact in result.artifacts:
    print(f"  - {artifact.filename} ({artifact.meta.get('author', 'unknown')})")

print("\nDirectories:")
for directory in result.directories:
    print(f"  - {directory}")
Use this to browse your file structure and discover what’s stored in each directory.
5

Update file metadata

Modify an artifact’s metadata without re-uploading the file.
# Update metadata
result = client.disks.artifacts.update(
    disk.id,
    file_path="/documents/2024/",
    filename="sprint-plan.md",
    meta={
        "department": "engineering",
        "author": "alice",
        "version": "1.1",
        "reviewed": True,
        "reviewed_by": "bob"
    }
)

print(f"Updated metadata: {result.artifact.meta}")
Metadata updates are efficient - you don’t need to re-upload the entire file.
6

Clean up

Delete artifacts and disks when you’re done.
# Delete an artifact
client.disks.artifacts.delete(
    disk.id,
    file_path="/documents/2024/",
    filename="sprint-plan.md"
)
print("Artifact deleted")

# Delete the entire disk
client.disks.delete(disk.id)
print("Disk deleted")
Deleting a disk removes all artifacts stored within it. This action cannot be undone.
You can skip this clean-up step and goto dashboard to view the artifacts (screenshot).

Complete example

Here’s a complete working example that demonstrates the full workflow:
from acontext import AcontextClient, FileUpload

def main():
    # Initialize client
    client = AcontextClient(
        api_key="sk-ac-your-root-api-bearer-token",
        base_url="http://localhost:8029/api/v1"
    )
    
    try:
        # Create disk
        disk = client.disks.create()
        print(f"✓ Created disk: {disk.id}")
        
        # Upload file
        artifact = client.disks.artifacts.upsert(
            disk.id,
            file=FileUpload(
                filename="notes.md",
                content=b"# Meeting Notes\nDiscussed Q4 goals."
            ),
            file_path="/meetings/",
            meta={"date": "2024-01-15", "attendees": 5}
        )
        print(f"✓ Uploaded: {artifact.filename}")
        
        # Retrieve with URL
        result = client.disks.artifacts.get(
            disk.id,
            file_path="/meetings/",
            filename="notes.md",
            with_public_url=True
        )
        print(f"✓ Download URL: {result.public_url}")
        
        # List files
        files = client.disks.artifacts.list(disk.id, path="/meetings/")
        print(f"✓ Found {len(files.artifacts)} file(s)")
        
        # Update metadata
        client.disks.artifacts.update(
            disk.id,
            file_path="/meetings/",
            filename="notes.md",
            meta={"date": "2024-01-15", "attendees": 5, "status": "completed"}
        )
        print("✓ Updated metadata")
        
        # Cleanup
        client.disks.artifacts.delete(disk.id, file_path="/meetings/", filename="notes.md")
        client.disks.delete(disk.id)
        print("✓ Cleaned up resources")
        
    except Exception as e:
        print(f"✗ Error: {e}")

if __name__ == "__main__":
    main()

Advanced features

Both SDKs support multiple file upload formats for convenience:
# Using FileUpload object (recommended)
file = FileUpload(filename="doc.txt", content=b"data")

# Using tuple with bytes
file = ("doc.txt", b"data")

# Using tuple with file handle
file = ("doc.txt", open("file.txt", "rb"))

# Using tuple with content type
file = ("doc.txt", b"data", "text/plain")
When you have many disks, use pagination to retrieve them in batches:
# List disks with pagination
result = client.disks.list(limit=10, time_desc=True)

for disk in result.items:
    print(f"Disk: {disk.id}")

# Get next page if available
if result.has_more:
    next_result = client.disks.list(
        limit=10,
        cursor=result.next_cursor,
        time_desc=True
    )
The Python SDK includes full async support for non-blocking operations:
import asyncio
from acontext import AsyncAcontextClient, FileUpload

async def main():
    async with AsyncAcontextClient(
        api_key="sk-ac-your-root-api-bearer-token",
        base_url="http://localhost:8029/api/v1"
    ) as client:
        # All methods are async
        disk = await client.disks.create()
        
        artifact = await client.disks.artifacts.upsert(
            disk.id,
            file=FileUpload(
                filename="async.txt",
                content=b"Async content"
            )
        )
        
        result = await client.disks.artifacts.get(
            disk.id,
            file_path="/",
            filename="async.txt"
        )
        
        await client.disks.delete(disk.id)

asyncio.run(main())
Each artifact includes system-generated metadata in the __artifact_info__ key:
{
  "__artifact_info__": {
    "size_bytes": 512,
    "content_type": "text/markdown",
    "last_modified": "2024-01-15T10:30:00Z"
  },
  "department": "engineering",
  "author": "alice"
}
You can access this information alongside your custom metadata.

Common use cases

Agent artifacts

Store files generated by AI agents, such as reports, diagrams, or code snippets, with metadata tracking generation parameters.

User uploads

Handle user-uploaded files with custom metadata for categorization, access control, and searchability.

Document management

Build a document repository with version tracking, metadata tagging, and organized folder structures.

Build artifacts

Store build outputs, logs, and assets from CI/CD pipelines with metadata for traceability.

Troubleshooting

Problem: Getting a 404 error when retrieving an artifact.Solution:
  • Verify the disk ID is correct
  • Ensure the file path exactly matches (including leading/trailing slashes)
  • Check that the filename is spelled correctly
  • Use artifacts.list() to see what files actually exist