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:
-
Keep the
packageIdand thenamefrom the response. Together they identify your submission. -
Call
GET /v2/sip/{packageId}/{submissionName}with these values. -
Repeat the call until the
statusisDONEorFAILED.
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_FOUNDorPROFILE_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 anS3SIP. -
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.failureMessageand thepluginReportstell 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:
-
Call
GET /v2/uploadurl. You get a pre-signedurl, akey, and anexpirytime. -
Upload the file with an HTTP
PUTto theurl, before theexpirytime. Don't send anAuthorizationheader: the URL carries its own signature. -
In your ingest call, add the file with
"type": "S3"and thekeyas itscontent.
By default, the upload URL is valid for five hours.
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 |
Size limit |
|---|---|---|
|
|
The file, encoded in base64. |
About 15 MB of files per request, because base64 makes a file a third larger. |
|
|
An |
By default, up to 200 MB (209,715,200 bytes). |
|
|
The |
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/htmlortext/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 |
|---|---|---|---|---|
|
|
header |
string |
No |
|
Body: a JSON object with these fields:
|
Name |
Type |
Required |
Description |
|---|---|---|---|
|
|
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. |
|
|
string |
No |
The ID for the package. If you leave it out, the archive creates one that starts with |
|
|
array of MetadataObject |
Yes |
The package-level metadata. Send an empty array if you have none. |
|
|
array of IngestContent |
No |
The files. Each file becomes a representation. |
|
|
No |
One file. The archive only uses it when |
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.
{
"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.
{
"packageId": "INV-2026-0042",
"packageType": "SIP",
"name": "nxblob-4821907635012873366.tmp",
"status": "WAITING",
"submissionDate": "2026-10-05T09:12:44.402Z"
}
-
400 INVALID_PACKAGE_ID: thepackageIdhas characters that aren't allowed.params.characterslists them. -
400 METADATA_SCHEMA_UNKNOWN: the archive doesn't know aschemathat you sent. -
400 INVALID_VALUE: a metadata file has no content, or it's larger than the size limit for metadata files.params.fieldnames the field, and for a file that's too largeparams.maxholds the limit in bytes. -
400 INVALID_VALUE: the archive can't download a metadata file from itsURL. -
400 INVALID_VALUE: a metadatanameisn't allowed, or two metadata entries in the same package or representation end up with the same file name.params.fieldismetadata.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 astitle,creator, anddescription. 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 typeBINARY,URL, orS3. The archive stores the file as it is. Iffile.contentis empty, the archive readsmetadatainstead.
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.
{
"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 |
|---|---|---|---|---|
|
|
path |
string |
Yes |
The ID of the package. |
|
|
header |
string |
No |
|
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 aname, see Name a metadata file.
{
"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 aschemathat 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 itsURL. -
400 INVALID_VALUE: a metadatanameisn'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 aname.
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.
{
"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 |
|---|---|---|---|---|
|
|
header |
string |
No |
|
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.
{
"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: withS3, 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: withS3, 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 |
|---|---|---|---|---|
|
|
query |
integer |
No |
The page number, starting at 0. Default: 0. |
|
|
query |
integer |
No |
The number of submissions per page, from 1 to 1000. Default: 100. |
|
|
query |
string |
No |
|
|
|
query |
string |
No |
|
|
|
query |
string |
No |
Shows the submissions whose title contains this text, in any case. |
|
|
query |
string |
No |
Shows the submissions whose package ID contains this text, in any case. |
|
|
query |
string |
No |
Shows the submissions whose name contains this text, in any case. |
|
|
query |
date-time |
No |
Shows the submissions sent at or after this time. |
|
|
query |
date-time |
No |
Shows the submissions sent at or before this time. A date without a time covers the whole day. |
|
|
query |
string |
No |
|
|
|
header |
string |
No |
|
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:pageis below 0, orpageSizeisn't between 1 and 1000. -
400 INVALID_SORT:sortOrderisn'tascordesc. An unknownsortBysorts bysubmissionDate. -
400 UNSUPPORTED_VALUE: astatusvalue isn'tWAITING,RUNNING, orFAILED. -
400 INVALID_VALUE:fromis afterto.
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 |
|---|---|---|---|---|
|
|
path |
string |
Yes |
The package ID of the submission. |
|
|
path |
string |
Yes |
The |
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.
{
"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 |
|---|---|---|---|---|
|
|
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 |
|---|---|---|---|---|
|
|
path |
string |
Yes |
The package ID of the submission. |
|
|
path |
string |
Yes |
The |
Response: 204 No Content.
-
404 PACKAGE_NOT_FOUND: there's no submission with this package ID and name. -
409 STATE_CONFLICT: the submission isn'tFAILED. You can only remove a failed submission.
Schemas
IngestDocument
The body of Ingest a document.
UpdateDocument
|
Field |
Type |
Description |
|---|---|---|
|
|
array of MetadataObject |
Required. The metadata to merge or replace. |
Content
|
Field |
Type |
Description |
|---|---|---|
|
|
string |
Required. |
|
|
string |
Required. The file in base64, its URL, or its upload key. |
|
|
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 |
|---|---|---|
|
|
array of MetadataObject |
Metadata for this file's representation only. |
MetadataObject
Has either metadata or file.
|
Field |
Type |
Description |
|---|---|---|
|
|
string |
Required. The namespace of the metadata schema. |
|
|
string |
The name of the XML file, up to 255 characters. The archive adds |
|
|
object |
The metadata as field names and values. A value can be a single value or a list. |
|
|
The complete metadata file. The |
ContentUrl
|
Field |
Type |
Description |
|---|---|---|
|
|
string |
The pre-signed URL to upload the file to, with |
|
|
string |
When the URL stops working, in ISO 8601 format (UTC). |
|
|
string |
The key to use as |
SIP
|
Field |
Type |
Description |
|---|---|---|
|
|
string |
The package ID of the submission. |
|
|
string |
Always |
|
|
string |
The submission name. |
|
|
string |
The title of the submission. |
|
|
string |
|
|
|
string |
Why the ingest failed, as a value from the error catalog. Only when the status is |
|
|
string |
A description of the failure for developers. Its wording can change, so don't parse it. Only when the status is |
|
|
date-time |
When the archive received the submission. |
|
|
date-time |
When the submission last changed. |
|
|
string |
The user name of the last user who changed the submission. |
|
|
array of PluginReport |
The results of the preservation actions. Only with the |
|
|
array of string |
Your permissions on the submission. Only in the list of submissions, with the |
SipList
|
Field |
Type |
Description |
|---|---|---|
|
|
number |
The number of submissions that match, on all pages. |
|
|
number |
The page number, starting at 0. |
|
|
number |
The number of submissions per page. |
|
|
array of SIP |
The submissions on this page. |
IngestStatus
|
Field |
Type |
Description |
|---|---|---|
|
|
string |
The package ID. |
|
|
string |
|
|
|
string |
The submission name, while the submission is in the queue. |
|
|
string |
|
|
|
string |
Why the ingest failed. Only when the status is |
|
|
string |
A description of the failure for developers. Only when the status is |
|
|
array of PluginReport |
The results of the preservation actions. When the status is |
PluginReport
One preservation action that ran on a submission or a package.
|
Field |
Type |
Description |
|---|---|---|
|
|
string |
The name of the action. |
|
|
date-time |
When the action ran. |
|
|
string |
|
|
|
string |
What the action found. |