Skip to content

Quickstart

Preserve one object, from nothing to version 1. Everything here is explained more fully elsewhere; this page is the shortest path that works.

There are two of them, and which is yours depends on one question: do you already have a METS file?

You need the API’s base URL, OAuth2 client credentials, and AWS credentials that can write to the deposit bucket. If you are missing any of those, ask whoever runs your instance — see Authentication.

For a system that manages and supplies its own METS — a digitisation workflow such as Goobi, or anything in a similar position. You are using the platform for versioned object storage, not to have it describe your content for you. Four requests.

import requests, boto3, time
API = "https://preservation-api.example"
WHOAMI = "my-integration" # sent as X-Client-Identity; recorded as who did what
token = requests.post(TOKEN_ENDPOINT, data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "scope": SCOPE,
}).json()["access_token"]
H = {"Authorization": f"Bearer {token}", "X-Client-Identity": WHOAMI}
# 1. A deposit: a working area in S3 that belongs to you.
# template None means the platform will not touch your METS - it only reads it.
deposit = requests.post(f"{API}/deposits", headers=H, json={
"type": "Deposit",
"template": "None",
"archivalGroup": f"{API}/repository/my-collection/my-first-object",
"archivalGroupName": "My first object",
}).json()
slug = deposit["id"].rsplit("/", 1)[-1]
# 2. Put your files in it, your METS among them. The API does not take bytes - you write to
# S3 yourself. Everything to be preserved goes in or below objects/; mets.xml sits at the root.
s3 = boto3.client("s3")
bucket, prefix = deposit["files"][5:].split("/", 1)
s3.upload_file("page-001.tif", bucket, f"{prefix}objects/page-001.tif")
s3.upload_file("mets.xml", bucket, f"{prefix}mets.xml")
# 3. Preserve it. Posting the diff's own URI runs the comparison and executes it in one go.
# The comparison reads your METS for each file's digest, size and content type.
result = requests.post(f"{API}/deposits/{slug}/importjobs", headers=H, json={
"id": f"{API}/deposits/{slug}/importjobs/diff",
}).json()
# 4. Wait for it.
while result["status"] not in ("completed", "completedWithErrors"):
time.sleep(2)
result = requests.get(result["id"], headers=H).json()
print(result["status"], result["newVersion"]) # completed v1

Your METS is preserved alongside the files, exactly as you wrote it.

What your METS has to say.

For every file, the platform needs a path, a SHA256 digest, a size and a content type — the digest is the part it cannot do without, because that is what it preserves against:

<premis:fixity>
<premis:messageDigestAlgorithm xsi:type="premis:messageDigestAlgorithm">SHA256</premis:messageDigestAlgorithm>
<premis:messageDigest>49ea24a3070c92a393289685ad9d3c3d71e5c23f0ac72e76e63aac658dc3ee59</premis:messageDigest>
</premis:fixity>

All of it must agree with the file actually in the workspace, or the diff refuses to be generated. See METS we read for everything the parser looks for, and Preserve for the first time for the long version of this page.

Either way, that object is now an OCFL v1 in S3, and GET /repository/my-collection/my-first-object will show it to you.

  1. A Deposit is good for exactly one Import Job. Once a job has run from it, asking for another diff answers 409. Every new version starts with a new Deposit.

  2. The parent Container must already exist. PUT /repository/a/b where a does not exist is refused with 409, naming the missing ancestor. Create it first.

  3. The API never uploads bytes for you. There is no endpoint that takes file content: you write to the deposit’s S3 prefix yourself, by whatever means suit you. That is deliberate — see the boundary.

If you want to… Read
The long version of either path above Preserve for the first time, a managed deposit
Change something already preserved Update with an export or without one
Upload a BagIt bag BagIt deposit
Do one specific small thing Recipes
Understand what any of these words mean Concepts