Upload & Fetch Workflow

This page walks through the most common FAIRSCAPE workflow end-to-end: authenticating, uploading an RO-Crate, polling until processing completes, and fetching the result by its ARK identifier.

Prerequisites

You need a FAIRSCAPE account and a valid RO-Crate packaged as a .zip file. Use fairscape-cli to create and package your crate locally.


Step 1 — Authenticate

Exchange your credentials for a Bearer token. All write operations require this token.

import requests

BASE_URL = "https://fairscape.net/api"

response = requests.post(
    f"{BASE_URL}/login",
    data={
        "username": "your_email@example.com",
        "password": "your_password"
    }
)
response.raise_for_status()

token = response.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
print("Authenticated. Token acquired.")
curl -X POST "https://fairscape.net/api/login" \
     -d "username=your_email@example.com&password=your_password"

Response:

{
  "access_token": "eyJ...",
  "token_type": "bearer"
}


Step 2 — Upload the RO-Crate

Upload your RO-Crate zip file. The server processes it asynchronously and returns a submissionUUID you can use to track progress.

crate_path = "/path/to/my-rocrate.zip"

with open(crate_path, "rb") as f:
    upload_response = requests.post(
        f"{BASE_URL}/rocrate/upload-async",
        files={"crate": ("my-rocrate.zip", f, "application/zip")},
        headers=headers
    )

upload_response.raise_for_status()
upload_job = upload_response.json()

submission_uuid = upload_job["guid"]
print(f"Upload started. Submission UUID: {submission_uuid}")
curl -X POST "https://fairscape.net/api/rocrate/upload-async" \
     -H "Authorization: Bearer <your_token>" \
     -F "crate=@/path/to/my-rocrate.zip;type=application/zip"

Response:

{
  "guid": "3f2a1c84-...",
  "status": "PENDING",
  "time_created": "2024-01-15T10:30:00Z"
}


Step 3 — Poll Upload Status

The server processes the zip file in the background (extracting metadata, minting identifiers, storing files). Poll the status endpoint until the job reaches COMPLETE.

import time

status_url = f"{BASE_URL}/rocrate/upload/status/{submission_uuid}"

while True:
    status_response = requests.get(status_url, headers=headers)
    status_response.raise_for_status()
    job = status_response.json()

    print(f"Status: {job['status']}")

    if job["status"] == "COMPLETE":
        rocrate_id = job["rocrate_id"]  # the ARK identifier
        print(f"Processing complete. RO-Crate ARK: {rocrate_id}")
        break
    elif job["status"] == "FAILED":
        raise RuntimeError(f"Upload failed: {job.get('error')}")

    time.sleep(5)
curl -X GET "https://fairscape.net/api/rocrate/upload/status/<submissionUUID>" \
     -H "Authorization: Bearer <your_token>"

Response (while processing):

{
  "guid": "3f2a1c84-...",
  "status": "PROCESSING",
  "rocrate_id": null
}

Response (when complete):

{
  "guid": "3f2a1c84-...",
  "status": "COMPLETE",
  "rocrate_id": "ark:59853/my-rocrate-2024"
}


Step 4 — Fetch by ARK Identifier

Once the crate is processed, fetch it by its ARK identifier using the universal resolver. This endpoint works for any FAIRSCAPE object (ROCrate, Dataset, Software, etc.).

resolve_response = requests.get(
    f"{BASE_URL}/{rocrate_id}",
    headers={"Accept": "application/json"}
)
resolve_response.raise_for_status()

metadata = resolve_response.json()
print(f"Name: {metadata['metadata']['name']}")
print(f"Description: {metadata['metadata']['description']}")
curl -X GET "https://fairscape.net/api/ark:59853/my-rocrate-2024" \
     -H "Accept: application/json"

Response:

{
  "@id": "ark:59853/my-rocrate-2024",
  "@type": "ROCrate",
  "metadata": {
    "@context": {
      "@vocab": "https://schema.org/",
      "EVI": "https://w3id.org/EVI#"
    },
    "name": "My Research RO-Crate",
    "description": "...",
    "keywords": ["proteomics", "b2ai"],
    "datePublished": "2024-01-15"
  }
}


Step 5 — Fetch ROCrate Metadata Directly

You can also use the ROCrate-specific endpoint, which returns the full RO-Crate JSON-LD structure. It also supports Croissant format via content negotiation.

# Standard RO-Crate JSON
rc_response = requests.get(
    f"{BASE_URL}/rocrate/{rocrate_id}",
    headers={"Accept": "application/json"}
)
rocrate_data = rc_response.json()

# Croissant format (for ML Commons compatibility)
croissant_response = requests.get(
    f"{BASE_URL}/rocrate/{rocrate_id}",
    headers={"Accept": "application/vnd.mlcommons-croissant+json"}
)
croissant_data = croissant_response.json()
# Standard JSON
curl -X GET "https://fairscape.net/api/rocrate/ark:59853/my-rocrate-2024" \
     -H "Accept: application/json"

# Croissant format
curl -X GET "https://fairscape.net/api/rocrate/ark:59853/my-rocrate-2024" \
     -H "Accept: application/vnd.mlcommons-croissant+json"

Complete Python Script

import requests
import time

BASE_URL = "https://fairscape.net/api"
CRATE_PATH = "/path/to/my-rocrate.zip"

# 1. Authenticate
login = requests.post(
    f"{BASE_URL}/login",
    data={"username": "you@example.com", "password": "your_password"}
)
login.raise_for_status()
token = login.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}

# 2. Upload
with open(CRATE_PATH, "rb") as f:
    upload = requests.post(
        f"{BASE_URL}/rocrate/upload-async",
        files={"crate": ("crate.zip", f, "application/zip")},
        headers=headers
    )
upload.raise_for_status()
submission_uuid = upload.json()["guid"]

# 3. Poll
while True:
    job = requests.get(
        f"{BASE_URL}/rocrate/upload/status/{submission_uuid}",
        headers=headers
    ).json()
    if job["status"] == "COMPLETE":
        rocrate_id = job["rocrate_id"]
        break
    elif job["status"] == "FAILED":
        raise RuntimeError(f"Failed: {job.get('error')}")
    time.sleep(5)

# 4. Fetch by ARK
result = requests.get(f"{BASE_URL}/{rocrate_id}").json()
print(f"RO-Crate '{result['metadata']['name']}' is live at: {rocrate_id}")