Skip to content

Preserve a digital object for the first time

Use case: preserve a digital object — a set of files that includes a METS file describing the object. The object does not yet exist in the repository; this will be version v1.

This is the workflow for consumers who manage and supply their own METS file. You use the platform for versioned object storage rather than for generating or managing the content of your objects, so none of the METS editing or pipeline operations come into it. If you would rather the platform wrote the METS for you, see Managed METS deposit instead.

  1. Make a Container to put it in, if you need one

    Section titled “Make a Container to put it in, if you need one”

    You can only create repository structure outside preserved objects; Containers within an object come from Import Jobs. So if the parent Container does not exist yet, make it first:

    GET /repository/library/c20-printed-books
    => 404 Not Found
    PUT /repository/library/c20-printed-books
    {
    "type": "Container",
    "name": "20th Century Printed Books"
    }
    => 201 Created

    See Repository for what you can and can’t do here.

  2. POST /deposits
    {
    "type": "Deposit",
    "archivalGroup": "https://preservation-api.example/repository/library/c20-printed-books/my-book",
    "archivalGroupName": "My Book",
    "submissionText": "A note from me for anyone interested"
    }

    Minimally an empty object is OK, though unusual — you can decide where the object goes later, and patch it in. But you do have to set archivalGroup before you can ask for an Import Job, so setting it now saves a step.

    Leave template unset (or send None): you are supplying the METS, and a template would have the platform write one.

    This returns 201 Created with the new Deposit as the body. The property that matters next is files:

    {
    "id": "https://preservation-api.example/deposits/szwettbzp4xw",
    "type": "Deposit",
    "archivalGroup": "https://preservation-api.example/repository/library/c20-printed-books/my-book",
    "archivalGroupExists": false,
    "files": "s3://working-bucket/deposits/szwettbzp4xw/",
    "status": "new",
    "template": "None",
    "active": true
    // ... the other Deposit properties
    }
  3. The files property is where the content of the new object goes. You interact directly with the AWS API (or, for another kind of backing store, whatever copying mechanism it offers) — there is no endpoint to POST bytes to.

    With no template the layout is yours to choose; the platform preserves what the METS describes. The convention the rest of the platform uses — and the one an exported object will come back in — is an objects/ folder for the content and a metadata/ folder for anything said about it, so following it will save you trouble later.

    The files must include a METS file in the root of the workspace that provides a SHA256 digest for each file, as PREMIS metadata attached to the file’s entry:

    <premis:fixity>
    <premis:messageDigestAlgorithm>SHA256</premis:messageDigestAlgorithm>
    <premis:messageDigest>49ea24a3070c92a393289685ad9d3c3d71e5c23f0ac72e76e63aac658dc3ee59</premis:messageDigest>
    </premis:fixity>

    You do not need to describe the METS file in its own METS: the platform adds it to the object itself, with a digest taken from storage.

  4. If the Deposit was created from an empty body, or you have changed your mind, patch archivalGroup, archivalGroupName or submissionText:

    PATCH /deposits/szwettbzp4xw
    {
    "archivalGroup": "https://preservation-api.example/repository/library/c20-printed-books/my-other-book",
    "archivalGroupName": "My Other Book",
    "submissionText": "I changed my mind about what this is called"
    }
  5. Create and execute an Import Job in a single operation

    Section titled “Create and execute an Import Job in a single operation”

    This is the shortcut for when you don’t need to see or modify the Import Job and can rely on the API to generate it for you:

    POST /deposits/szwettbzp4xw/importjobs
    {
    "id": "/deposits/szwettbzp4xw/importjobs/diff"
    }

    The returned object is an ImportJobResult at 201 Created:

    {
    "id": "https://preservation-api.example/deposits/szwettbzp4xw/importjobs/results/ge7n4ds2",
    "type": "ImportJobResult",
    "importJob": "https://preservation-api.example/deposits/szwettbzp4xw/importjobs/transient/638950821343621834",
    "originalImportJob": "https://preservation-api.example/deposits/szwettbzp4xw/importjobs/diff",
    "deposit": "https://preservation-api.example/deposits/szwettbzp4xw",
    "archivalGroup": "https://preservation-api.example/repository/library/c20-printed-books/my-other-book",
    "status": "waiting",
    "dateBegun": null,
    "dateFinished": null,
    "sourceVersion": null,
    "newVersion": null,
    "errors": null,
    "containersAdded": [],
    "binariesAdded": []
    // ... the other result lists
    }
  6. GET /deposits/szwettbzp4xw/importjobs/results/ge7n4ds2

    You can do this as little or often as you like — or even not at all. Over time you expect status to become completed, newVersion to become v1, and binariesAdded and its neighbours to fill up.

    A completedWithErrors means nothing was preserved; errors says why. The Deposit becomes inactive either way, and you can reactivate it to try again.

  7. Whether it’s one you have just created or one that has existed for years, this is no different from browsing any other part of the repository:

    GET /repository/library/c20-printed-books/my-other-book

If the platform is integrated with an identity service that already knows where your object belongs, you can skip deciding on a path and a title. POST the identifier instead of a Deposit:

POST /deposits/from-identifier
{
"schema": "catirn",
"value": "1000001"
}

The Deposit comes back with archivalGroup and archivalGroupName already filled in — here, the API consulted an external service to learn that 1000001 should be preserved at .../library/c18-printed-books/a-10000001 and that its title is “A Treatise on Opticks”. From that point on the flow is exactly the same, without step 4. See Creating a Deposit from an identifier for the schemas.