Quickstart

Push your first version in 5 minutes. All you need is curl and a running Underlay instance.

1. Create an account

curl -X POST https://underlay.org/api/accounts/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "your-password",
    "username": "yourname",
    "displayName": "Your Name"
  }'

2. Create an API key

Log in and create a write-scoped key:

# Login (saves session cookie)
curl -X POST https://underlay.org/api/accounts/login \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"email": "[email protected]", "password": "your-password"}'

# Create API key
curl -X POST https://underlay.org/api/accounts/keys \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"label": "my-app", "scope": "write"}'
# → {"id":"...","key":"ul_abc123...","label":"my-app","scope":"write"}

Save the key value — it's shown only once.

3. Create a collection

export KEY="ul_abc123..."

curl -X POST https://underlay.org/api/accounts/yourname/collections \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $KEY" \
  -d '{
    "slug": "my-dataset",
    "name": "My Dataset",
    "description": "A test collection",
    "public": true
  }'

4. Push a version

curl -X POST https://underlay.org/api/collections/yourname/my-dataset/versions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $KEY" \
  -d '{
    "base_version": null,
    "message": "Initial import",
    "app_id": "my-app",
    "schema": {
      "type": "object",
      "properties": {
        "Book": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "author": {"type": "string"},
            "year": {"type": "integer"}
          }
        }
      }
    },
    "changes": {
      "added": [
        {"id": "book-1", "type": "Book", "data": {"title": "Gödel, Escher, Bach", "author": "Douglas Hofstadter", "year": 1979}},
        {"id": "book-2", "type": "Book", "data": {"title": "The Structure of Scientific Revolutions", "author": "Thomas Kuhn", "year": 1962}}
      ]
    }
  }'
# → {"version":1,"semver":"v1.0.0","hash":"...","recordCount":2,"fileCount":0}

5. Read it back

# Get collection info
curl https://underlay.org/api/collections/yourname/my-dataset

# Get version 1 records
curl https://underlay.org/api/collections/yourname/my-dataset/versions/1/records

# Get the manifest
curl https://underlay.org/api/collections/yourname/my-dataset/versions/1/manifest

6. Push an update

curl -X POST https://underlay.org/api/collections/yourname/my-dataset/versions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $KEY" \
  -d '{
    "base_version": 1,
    "message": "Add third book, fix year",
    "changes": {
      "added": [
        {"id": "book-3", "type": "Book", "data": {"title": "Philosophical Investigations", "author": "Ludwig Wittgenstein", "year": 1953}}
      ],
      "updated": [
        {"id": "book-1", "type": "Book", "data": {"title": "Gödel, Escher, Bach", "author": "Douglas Hofstadter", "year": 1979}}
      ]
    }
  }'
# → {"version":2,"semver":"v1.1.0","hash":"...","recordCount":3,"fileCount":0}

7. Diff versions

curl https://underlay.org/api/collections/yourname/my-dataset/versions/2/diff?from=1
# → {"from":1,"to":2,"added":[...],"updated":[...],"removed":[]}

Working with files

To attach files (PDFs, images, etc.) to records, upload them first by hash:

# Compute hash
HASH=$(shasum -a 256 paper.pdf | cut -d' ' -f1)

# Upload
curl -X PUT "https://underlay.org/api/collections/yourname/my-dataset/files/sha256:$HASH" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/pdf" \
  --data-binary @paper.pdf

# Reference in a record
# {"id": "book-1", "type": "Book", "data": {"title": "...", "pdf": {"$file": "sha256:..."}}}

Next steps