26.3.0 26.2.0 26.1.0 25.4.0 25.3.0
26.3.0 26.2.0 26.1.0 25.4.0 25.3.0

Ingest API

You can use the Ingest API to add documents and SIPs to the archive and to update a package's descriptive metadata. You can then follow each submission until it's done. Ingest is asynchronous. A successful response means that the archive accepted your submission, not that the package is ready.

Each ingest call creates a submission: one SIP that waits in the archive's ingest queue until the archive processes it. You can ingest in two ways:

  • Send a document: you have files and metadata, and the archive builds the SIP for you. Use PUT /v2/document.

  • Send a SIP: you already have a ZIP file in the E-ARK SIP format. Use PUT /v2/sip.

Every call needs the headers in Common headers, and errors use the shape described in Errors. Some calls don't need a specific role. Every user who can call the API can use them, and you only see what your access to the packages allows. For where roles come from, see Authentication.

Follow a submission until it's done

Every ingest call returns a SIP object with the status WAITING:

  1. Keep the packageId and the name from the response. Together they identify your submission.

  2. Call GET /v2/sip/{packageId}/{submissionName} with these values.

  3. Repeat the call until the status is DONE or FAILED.

If the packageId starts with urn:temp:, see Ingest a SIP.

The status is one of the following:

  • WAITING: the submission is in the queue.

  • RUNNING: the archive is processing it.

  • DONE: the archive ingested the submission into the package.

  • FAILED: the ingest failed. The submission stays in the queue, and you can reingest it or remove it.

Note: After DONE, the package can still wait for a QA reviewer. This happens when its profile requires a QA review, or when a preservation action flags it. To check, retrieve the package and look at its state, as described in Dissemination API.

When a submission fails, failureReason says why, as one of these values from the error catalog in Errors:

  • SIP_INVALID: the SIP isn't a valid E-ARK SIP.

  • PROFILE_NOT_FOUND or PROFILE_DISABLED: the archive doesn't have the profile, or the profile is turned off.

  • SUBMISSION_ALREADY_EXISTS: the package already has a submission with this name. You can only get this with an S3 SIP.

  • PERMISSION_DENIED: you don't have the rights to add this package, or to update it.

  • CONTENT_NOT_FOUND: the archive couldn't find the uploaded file.

  • INGEST_FAILED: something else went wrong. failureMessage and the pluginReports tell you more.

How a large-file upload works

The request body of an ingest call is limited in size, so large files go to the archive's upload storage first. You then point to them in the ingest call:

  1. Call GET /v2/uploadurl. You get a pre-signed url, a key, and an expiry time.

  2. Upload the file with an HTTP PUT to the url, before the expiry time. Don't send an Authorization header: the URL carries its own signature.

  3. In your ingest call, add the file with "type": "S3" and the key as its content.

By default, the upload URL is valid for five hours.

Bash
curl -X PUT --upload-file invoice-2026-0042.pdf "https://ingest.example.com/3f0c2a9e-5b7d-4e1a-9c2f-8d6b1a0e4c77?X-Amz-Signature=..."

Content types and size limits

Wherever the API takes a file (a document, a SIP, or a metadata file), you send it as a content object with a type. Pick the type that matches where your file is:

Type

What you put in content

Size limit

BINARY

The file, encoded in base64.

About 15 MB of files per request, because base64 makes a file a third larger.

URL

An http or https URL that the archive downloads the file from.

By default, up to 200 MB (209,715,200 bytes).

S3

The key that you got from GET /v2/uploadurl.

Up to 5 GB per upload. For larger files, ask Docbyte about multipart uploads.

A few things to know about URL:

  • The host must be reachable on the public internet. The archive refuses private and local addresses, and URLs with a user name or password in them.

  • The archive follows up to three redirects.

  • The archive refuses a response with the content type text/html or text/plain.

  • By default, the request times out after 20 seconds.

  • Your environment can limit the hosts that the archive downloads from to a list. If a host is refused, ask Docbyte.

A metadata file has its own, smaller limit. By default, a metadata file that you send as URL or S3 can be up to 50 MB (52,428,800 bytes). A larger file fails with 400 INVALID_VALUE. If you need a higher limit, ask Docbyte.

By default, a request body can be up to 21 MB, and one value up to 20,000,000 characters. A request that's too large fails with 413 and the reason BODY_TOO_LARGE or STRING_TOO_LARGE.

Ingest a document

You can send files and metadata, and the archive builds a SIP from them and submits it for ingest. Each file becomes a representation in the package. If the archive already has a package with the packageId that you send, the archive adds the files to that package.

PUT /v2/document

Required role: archive-ingest

Name

In

Type

Required

Description

enrichers

header

string

No

pluginreports, to add the results of the preservation actions. See enrichers.

Body: a JSON object with these fields:

Name

Type

Required

Description

profile

string

Yes

The URN of the profile. A profile decides how the archive stores the package, including its retention. You get the valid values from Docbyte, or from the profile list in the Admin API.

packageId

string

No

The ID for the package. If you leave it out, the archive creates one that starts with uuid-. For the allowed characters, see Choose a package ID.

metadatas

array of MetadataObject

Yes

The package-level metadata. Send an empty array if you have none.

documents

array of IngestContent

No

The files. Each file becomes a representation.

document

IngestContent

No

One file. The archive only uses it when documents is empty.

If you don't send Dublin Core metadata, the archive adds it for you, with the name of the first file as the title.

The archive cleans up file names. It drops accents and other characters outside ASCII. It replaces characters such as / and ? with _, and removes tabs, line breaks, and spaces at both ends.

JSON
{
  "profile": "urn:example:profile:invoices",
  "packageId": "INV-2026-0042",
  "metadatas": [
    {
      "schema": "http://purl.org/dc/elements/1.1/",
      "metadata": {
        "title": "Invoice 2026-0042",
        "creator": "Finance department"
      }
    }
  ],
  "documents": [
    {
      "type": "S3",
      "content": "3f0c2a9e-5b7d-4e1a-9c2f-8d6b1a0e4c77",
      "name": "invoice-2026-0042.pdf"
    },
    {
      "type": "BINARY",
      "content": "JVBERi0xLjcKJcfsj6IK",
      "name": "payment-terms.pdf",
      "metadatas": [
        {
          "schema": "http://purl.org/dc/elements/1.1/",
          "metadata": {
            "title": "Payment terms"
          }
        }
      ]
    }
  ]
}

Response: 200 OK with a SIP object for your submission, with the status WAITING. The archive picks the submission name, so use the one from the response to follow the submission.

JSON
{
  "packageId": "INV-2026-0042",
  "packageType": "SIP",
  "name": "nxblob-4821907635012873366.tmp",
  "status": "WAITING",
  "submissionDate": "2026-10-05T09:12:44.402Z"
}
  • 400 INVALID_PACKAGE_ID: the packageId has characters that aren't allowed. params.characters lists them.

  • 400 METADATA_SCHEMA_UNKNOWN: the archive doesn't know a schema that you sent.

  • 400 INVALID_VALUE: a metadata file has no content, or it's larger than the size limit for metadata files. params.field names the field, and for a file that's too large params.max holds the limit in bytes.

  • 400 INVALID_VALUE: the archive can't download a metadata file from its URL.

  • 400 INVALID_VALUE: a metadata name isn't allowed, or two metadata entries in the same package or representation end up with the same file name. params.field is metadata.name.

Choose a package ID

A package ID that you choose can only use ASCII characters. It can't contain any of these characters:

  • @ < > : " / \ | ? *

  • A tab or a line break

If you need one of these characters, ask Docbyte: they can allow more characters for your environment.

Send metadata

A metadata object always has a schema: the namespace of the metadata schema, for example http://purl.org/dc/elements/1.1/ for Dublin Core. You then give the metadata in one of two ways:

  • As fields: put the values in metadata, as field names and values. For Dublin Core, the field names are the element names, such as title, creator, and description. A field can have one value or a list of values.

  • As a file: put the complete metadata file in file, as a content object of the type BINARY, URL, or S3. The archive stores the file as it is. If file.content is empty, the archive reads metadata instead.

Metadata in metadatas on the request applies to the package. Metadata in metadatas on a document applies to that document's representation only.

With URL or S3, the archive fetches the file for you. It can be up to 50 MB by default, as described in Content types and size limits.

Name a metadata file

A metadata object can have a name: the name of the XML file that the archive stores. If you leave it out, the archive names the file after its schema, for example dc.xml for Dublin Core.

  • The name can be up to 255 characters, and it must end in .xml. If you leave out the extension, the archive adds .xml.

  • The name is a plain file name. It can't contain a path, a :, or any of the characters @ < > " / \ | ? *, a tab, or a line break. It can only use ASCII characters, and it can't start or end with a space.

  • The name must be unique within its package, or within its representation. The check ignores upper and lower case.

If you want several metadata files for one schema, give each one its own name.

JSON
{
  "schema": "http://purl.org/dc/elements/1.1/",
  "name": "invoice-details.xml",
  "file": {
    "type": "S3",
    "content": "6d2b9f10-4c7e-4a55-8b13-0e9a7c5d2f81",
    "name": "invoice-details.xml"
  }
}

Note: The archive keeps one set of searchable values for each schema. When several files share a schema, only one of them shows up in the searchable values.

To find the schema namespaces that your organization uses, ask Docbyte.

Update a package's metadata

You can replace or add descriptive metadata on a package that's already in the archive. Like an ingest, the update is a submission that the archive processes in the background.

POST /v2/document/{packageId}

Required role: archive-ingest

Name

In

Type

Required

Description

packageId

path

string

Yes

The ID of the package.

enrichers

header

string

No

pluginreports, to add the results of the preservation actions. See enrichers.

Body: an UpdateDocument object with a metadatas array of MetadataObject items.

The way you send the metadata decides what happens to the schema's metadata on the package:

  • As fields: the archive merges your fields into the package's current metadata for that schema. Fields that you don't send keep their value.

  • As a file: your file replaces the package's metadata file for that schema.

  • A schema that the package doesn't have: the archive adds it.

  • With a name: the entry replaces the stored file as a whole, also when you send fields. The archive doesn't fill in fields that you leave out, so send every field that you want to keep. For the rules for a name, see Name a metadata file.

JSON
{
  "metadatas": [
    {
      "schema": "http://purl.org/dc/elements/1.1/",
      "metadata": {
        "description": "Paid on 2026-09-30"
      }
    }
  ]
}

Response: 200 OK with a SIP object for the update. Use the packageId and the name from the response to follow it like any other submission.

  • 400 METADATA_SCHEMA_UNKNOWN: the archive doesn't know a schema that you sent.

  • 400 INVALID_VALUE: a metadata file has no content, or it's larger than the size limit for metadata files.

  • 400 INVALID_VALUE: the archive can't download a metadata file from its URL.

  • 400 INVALID_VALUE: a metadata name isn't allowed, or two entries in the request have the same file name.

  • 404 RESOURCE_NOT_FOUND: there's no package with this ID.

  • 409 METADATA_NOT_READY: the archive is still processing the package's ingest, so it can't merge fields. Try again a bit later, or send the complete metadata as a file, or give the entry a name.

Request an upload URL

You can request a pre-signed URL to upload a large file, together with the key to reference it in an ingest call. For the whole flow, see How a large-file upload works.

GET /v2/uploadurl

Required role: archive-ingest

Response: 200 OK with a ContentUrl object.

JSON
{
  "url": "https://ingest.example.com/3f0c2a9e-5b7d-4e1a-9c2f-8d6b1a0e4c77?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=...",
  "expiry": "2026-10-05T14:12:44Z",
  "key": "3f0c2a9e-5b7d-4e1a-9c2f-8d6b1a0e4c77"
}

Request only the upload URL

GET /v2/url

Required role: archive-ingest

Use GET /v2/uploadurl instead, because it also returns the key and the expiry. This call returns only the pre-signed URL, as a JSON string. The key is the last part of the URL's path.

Ingest a SIP

You can submit a SIP that you already have, as a ZIP file in the E-ARK SIP format. The archive reads the package ID and the profile from the SIP itself. If the archive already has a package with that package ID, the SIP updates that package.

PUT /v2/sip

Required role: archive-ingest

Name

In

Type

Required

Description

enrichers

header

string

No

pluginreports, to add the results of the preservation actions. See enrichers.

Body: a Content object. For large SIPs, use S3. See Content types and size limits.

With S3, the name becomes the submission name, so use a different name for each SIP that you send to the same package. With BINARY or URL, the archive picks the submission name. Use the name from the response.

JSON
{
  "type": "S3",
  "content": "8a1d4e7f-2c3b-4f6a-9e0d-1b2c3d4e5f60",
  "name": "INV-2026-0042-update-1.zip"
}

Response: 200 OK with a SIP object for your submission.

With S3, the archive normally reads the package ID from your SIP right away, so the response holds it. If it can't, the response holds a temporary ID, like it does with BINARY or URL.

A temporary ID starts with urn:temp:. The archive replaces it with the package ID from your SIP's METS file (its OBJID) once it has read the SIP. If a call with the temporary ID returns 404, follow the submission with the OBJID and the name from the response.

  • 400 INVALID_REQUEST: with S3, the profile of the SIP is disabled or unknown.

  • 404 CONTENT_NOT_FOUND: there's no uploaded file with this key.

  • 409 SUBMISSION_ALREADY_EXISTS: with S3, the package already has a submission with this name, in the archive or in the queue. If the archive can't read the package ID from the SIP right away, the submission fails with this reason later.

List submissions

You can list the submissions that are still in the ingest queue: the ones that are waiting, running, or failed. A submission leaves the queue when its ingest succeeds.

GET /v2/sip

Required role: Any role.

Name

In

Type

Required

Description

page

query

integer

No

The page number, starting at 0. Default: 0.

pageSize

query

integer

No

The number of submissions per page, from 1 to 1000. Default: 100.

sortBy

query

string

No

title, packageId, submissionName, status, lastModified, or submissionDate. Default: submissionDate.

sortOrder

query

string

No

asc or desc. Default: desc.

title

query

string

No

Shows the submissions whose title contains this text, in any case.

packageId

query

string

No

Shows the submissions whose package ID contains this text, in any case.

submissionName

query

string

No

Shows the submissions whose name contains this text, in any case.

from

query

date-time

No

Shows the submissions sent at or after this time.

to

query

date-time

No

Shows the submissions sent at or before this time. A date without a time covers the whole day.

status

query

string

No

WAITING, RUNNING, or FAILED. To ask for several, separate them with commas: status=WAITING,FAILED.

enrichers

header

string

No

pluginreports, permissions. See enrichers.

Bash
curl "https://archive.example.com/v2/sip?status=FAILED&from=2026-10-01T00:00:00Z&pageSize=50" \
  -H "Authorization: Bearer $TOKEN" \
  -H "tenant: example-tenant" \
  -H "enrichers: pluginreports"

Response: 200 OK with a SipList object.

  • 400 OUT_OF_RANGE: page is below 0, or pageSize isn't between 1 and 1000.

  • 400 INVALID_SORT: sortOrder isn't asc or desc. An unknown sortBy sorts by submissionDate.

  • 400 UNSUPPORTED_VALUE: a status value isn't WAITING, RUNNING, or FAILED.

  • 400 INVALID_VALUE: from is after to.

Get the status of a submission

You can check where one submission is, and see the results of the preservation actions that ran on it.

GET /v2/sip/{packageId}/{submissionName}

Required role: Any role.

Name

In

Type

Required

Description

packageId

path

string

Yes

The package ID of the submission.

submissionName

path

string

Yes

The name of the submission.

Response: 200 OK with an IngestStatus object. It always includes the pluginReports, if any actions ran. For the values of status, see Follow a submission until it's done. When it's FAILED, failureReason and failureMessage say why.

JSON
{
  "packageId": "INV-2026-0042",
  "packageType": "SIP",
  "name": "INV-2026-0042-update-1.zip",
  "status": "FAILED",
  "failureReason": "SUBMISSION_ALREADY_EXISTS",
  "failureMessage": "AIP already contains a submission of name INV-2026-0042-update-1.zip"
}
  • 404: there's no submission with this package ID and name.

Reingest a submission

You can restart the submission of a package, for example after you fixed the cause of a failure. The archive keeps the content that you sent, so you don't send it again.

POST /v2/sip/{packageId}/reingest

Required role: Any role. You need the rights to ingest or update the package.

Name

In

Type

Required

Description

packageId

path

string

Yes

The package ID of the submission.

Response: 202 Accepted with the SIP object of the submission. The status in the response is the one from before the call: follow the submission to see it change.

  • 404 RESOURCE_NOT_FOUND: there's no submission in the queue for this package ID.

Remove a failed submission

You can take a failed submission out of the ingest queue. If the package has several submissions in the queue, the archive only removes the one that you name.

DELETE /v2/sip/{packageId}/{submissionName}

Required role: archive-ingest

Name

In

Type

Required

Description

packageId

path

string

Yes

The package ID of the submission.

submissionName

path

string

Yes

The name of the submission.

Response: 204 No Content.

  • 404 PACKAGE_NOT_FOUND: there's no submission with this package ID and name.

  • 409 STATE_CONFLICT: the submission isn't FAILED. You can only remove a failed submission.

Schemas

IngestDocument

The body of Ingest a document.

UpdateDocument

Field

Type

Description

metadatas

array of MetadataObject

Required. The metadata to merge or replace.

Content

Field

Type

Description

type

string

Required. BINARY, URL, or S3.

content

string

Required. The file in base64, its URL, or its upload key.

name

string

Required. The file name, at least three characters. For a SIP, see Ingest a SIP.

IngestContent

A Content object with one more field:

Field

Type

Description

metadatas

array of MetadataObject

Metadata for this file's representation only.

MetadataObject

Has either metadata or file.

Field

Type

Description

schema

string

Required. The namespace of the metadata schema.

name

string

The name of the XML file, up to 255 characters. The archive adds .xml if it's missing. It must be unique within its package or representation. See Name a metadata file.

metadata

object

The metadata as field names and values. A value can be a single value or a list.

file

Content

The complete metadata file. The type can be BINARY, URL, or S3.

ContentUrl

Field

Type

Description

url

string

The pre-signed URL to upload the file to, with PUT.

expiry

string

When the URL stops working, in ISO 8601 format (UTC).

key

string

The key to use as content with "type": "S3".

SIP

Field

Type

Description

packageId

string

The package ID of the submission.

packageType

string

Always SIP.

name

string

The submission name.

title

string

The title of the submission.

status

string

WAITING, RUNNING, or FAILED.

failureReason

string

Why the ingest failed, as a value from the error catalog. Only when the status is FAILED.

failureMessage

string

A description of the failure for developers. Its wording can change, so don't parse it. Only when the status is FAILED.

submissionDate

date-time

When the archive received the submission.

lastModified

date-time

When the submission last changed.

lastContributor

string

The user name of the last user who changed the submission.

pluginReports

array of PluginReport

The results of the preservation actions. Only with the pluginreports enricher.

permissions

array of string

Your permissions on the submission. Only in the list of submissions, with the permissions enricher.

SipList

Field

Type

Description

totalResults

number

The number of submissions that match, on all pages.

page

number

The page number, starting at 0.

pageSize

number

The number of submissions per page.

items

array of SIP

The submissions on this page.

IngestStatus

Field

Type

Description

packageId

string

The package ID.

packageType

string

SIP while the submission is in the queue, AIP when it's done.

name

string

The submission name, while the submission is in the queue.

status

string

WAITING, RUNNING, FAILED, or DONE.

failureReason

string

Why the ingest failed. Only when the status is FAILED.

failureMessage

string

A description of the failure for developers. Only when the status is FAILED.

pluginReports

array of PluginReport

The results of the preservation actions. When the status is DONE, these are the package's results.

PluginReport

One preservation action that ran on a submission or a package.

Field

Type

Description

pluginName

string

The name of the action.

executionDate

date-time

When the action ran.

status

string

SUCCESS, WARNING, or FAILURE.

message

string

What the action found.