Skip to content

Quickstart

This guide walks you through signing up for Tilde, creating an organization and repository, uploading your first object, and committing it.

1. Sign Up

Sign up for Tilde

2. Authenticate

For programmatic access, create an API key in the Tilde web UI or via the REST API. The token is shown only once — save it securely.

# Install the CLI
curl -fsSL https://tilde.run/install | sh

# Authenticate via your browser
tilde auth login
pip install tilde-sdk
import tilde

tilde.configure(api_key="YOUR_API_TOKEN")
# All subsequent requests use the Authorization header
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://tilde.run/api/v1/auth/me

See the CLI docs, Python SDK, or Using the API for full documentation on authentication methods.

3. Create an Organization

Organizations are the top-level grouping for repositories, connectors, and members. Organization names must be lowercase alphanumeric with hyphens (^[a-z0-9][a-z0-9-]{1,62}$).

org = tilde.organizations.create(
    "my-team",
    display_name="My Team",
)
print(org.name)
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X POST https://tilde.run/api/v1/organizations \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-team",
    "display_name": "My Team"
  }'

You are automatically added as the owner of the new organization.

4. Create a Repository

Repositories hold your versioned data. Create one in the Tilde web UI or via the API:

org = tilde.organizations.get("my-team")
org.repositories.create(
    "my-data",
    description="My first data repository",
)

# Access the repository with the SDK
repo = tilde.repository("my-team/my-data")
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X POST https://tilde.run/api/v1/organizations/my-team/repositories \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-data",
    "description": "My first data repository"
  }'

5. Run Code in a Sandbox

The fastest way to work with your data is to run a sandbox. Tilde mounts your repository inside an isolated container at /sandbox, your code reads and writes files there as if it were a normal filesystem, and any changes are captured as a transactional session that is committed atomically on success.

repo = tilde.repository("my-team/my-data")

# Interactive shell — run multiple commands in one sandbox
with repo.shell(image="python:3.12") as sh:
    # Write a new object into the repository
    sh.run("mkdir -p /sandbox/data && echo 'Hello, Tilde!' > /sandbox/data/hello.txt")

    # Read it back
    result = sh.run("cat /sandbox/data/hello.txt")
    print(result.stdout.text())  # Hello, Tilde!

# On clean exit, all writes under /sandbox are committed atomically.
# If an exception is raised inside the block, nothing is committed.

For a single command without an interactive shell, use repo.execute():

result = repo.execute(
    "python -c \"open('/sandbox/data/hello.txt','w').write('Hello, Tilde!')\"",
    image="python:3.12",
)
print(result.exit_code)
# Interactive: open a shell inside a sandbox
tilde shell my-team/my-data --image python:3.12
# then, inside the sandbox:
#   echo "Hello, Tilde!" > /sandbox/data/hello.txt
#   cat /sandbox/data/hello.txt
# exit the shell to commit; Ctrl-C to roll back

# One-shot: run a command and stream output
tilde exec my-team/my-data -- \
  sh -c 'echo "Hello, Tilde!" > /sandbox/data/hello.txt'
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X POST https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sandboxes \
  -H "Content-Type: application/json" \
  -d '{
    "image": "python:3.12",
    "command": ["sh", "-c", "echo Hello, Tilde! > /sandbox/data/hello.txt"]
  }'

Transactional by default

All filesystem changes made inside a sandbox happen in a session. Only successful runs are committed atomically — if the sandbox exits non-zero or is cancelled, nothing changes. See the Sandboxes guide for triggers, network policy, and more.

6. Programmatically Create a Session (Advanced)

If you need direct programmatic control over individual object operations from your own process — without launching a sandbox — work with a session directly. A session is a transactional workspace: stage changes, then commit or roll back.

with repo.session() as session:
    # Stage one or more changes
    session.objects.put("data/hello.txt", b"Hello, Tilde!")
    session.objects.put("data/notes.md", b"# Notes\n")

    # Commit makes all staged changes permanent
    commit_id = session.commit("Add hello.txt and notes.md")
    print(f"Committed: {commit_id}")
# An exception inside the block rolls the session back automatically.
# 1. Create a session
SESSION_ID=$(curl -s -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X POST https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sessions \
  | jq -r .session_id)

# 2. Upload an object into the session (3-step flow)
# Step 2a: Get an upload URL
PUT_RESP=$(curl -s -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X PUT "https://tilde.run/api/v1/organizations/my-team/repositories/my-data/object?session_id=$SESSION_ID&path=data/hello.txt")
UPLOAD_URL=$(echo "$PUT_RESP" | jq -r .upload_url)
UPLOAD_TOKEN=$(echo "$PUT_RESP" | jq -r .upload_token)

# Step 2b: Upload the object body to the upload URL
ETAG=$(echo -n "Hello, Tilde!" | curl -s -X PUT -H "Content-Type: text/plain" \
  --data-binary @- "$UPLOAD_URL" -D - -o /dev/null \
  | grep -i '^etag:' | tr -d '\r' | cut -d' ' -f2 | tr -d '"')

# Step 2c: Finalize to register in the session
curl -s -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X POST "https://tilde.run/api/v1/organizations/my-team/repositories/my-data/object/finalize?session_id=$SESSION_ID&path=data/hello.txt" \
  -H "Content-Type: application/json" \
  -d "{\"upload_token\": \"$UPLOAD_TOKEN\", \"checksum\": \"$ETAG\", \"content_type\": \"text/plain\"}"

# 3. Commit
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X POST "https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sessions/$SESSION_ID" \
  -H "Content-Type: application/json" \
  -d '{"message": "Add hello.txt"}'

7. Browse the Commit Log

Every commit -- whether from a sandbox or a session -- lands on the repository's commit log. List commits and read objects from any snapshot:

# Walk commits, newest first
for commit in repo.commits.list():
    print(commit.id[:12], commit.committer, commit.message)

    # Read an object from this commit
    with commit.objects.get("data/hello.txt") as f:
        print(f.read().decode())  # Hello, Tilde!
    break
# List commits
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  "https://tilde.run/api/v1/organizations/my-team/repositories/my-data/commits"

# Read an object from the latest commit
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  "https://tilde.run/api/v1/organizations/my-team/repositories/my-data/object?path=data/hello.txt"

Next Steps

  • Sandboxes — run code against your data in isolated containers, with automatic triggers for event-driven pipelines
  • CLI — install and use the tilde command-line tool for sandboxes, interactive shells, and scripting
  • Python SDK — full guide to the Python SDK including sandbox execution, sessions, objects, streaming, and error handling
  • Using the API — learn about all authentication methods, pagination, and error handling
  • API Reference — full reference for every endpoint
  • Connectors Reference — import data from S3 or GCS
  • RBAC Reference — set up groups and policies to control access