Skip to content
Application Programming Interface

Application Programming Interface

Overview

The Digital Preservation services at the National Library of Norway provide a RESTful API for managing the submission and dissemination of digital objects. This allows clients to integrate their systems with our preservation workflows.

This documentation provides a high-level overview of the available APIs. For complete technical details, please refer to our Swagger UI.

The machine-readable OpenAPI specification (JSON) is available at https://api.nb.no/dps-submission/v1/api-docs.

The API lets you:

This page also lists the endpoints, request fields, and status lifecycles for submissions and disseminations, and the messages sent through webhooks.

Authentication & Authorization

The API uses OAuth 2.0 with JWT tokens for authentication, specifically implementing the OAuth 2.0 Client Credentials flow. The services are designed for system-to-system communication and do not support user-based authentication flows (e.g., the Authorization Code flow).

OAuth 2.0 Client Credentials Flow

  1. Your client application authenticates with the authorization server using its client ID and client secret.
  2. The authorization server validates these credentials and returns an access token.
  3. Your application includes this access token in the Authorization header of all API requests.

All API communication is handled via https://api.nb.no, while authentication requests are sent to https://www.nb.no/authn/realms/dps/protocol/openid-connect/token.

Example token request:

POST /authn/realms/dps/protocol/openid-connect/token HTTP/1.1
Host: www.nb.no
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id={your_client_id}
&client_secret={your_client_secret}

Example API request with token:

GET /dps-submission/v1/contracts/{your_contract_id}/submissions/{your_submission_id} HTTP/1.1
Host: api.nb.no
Authorization: Bearer {access_token}

Role-Based Authorization

Access to endpoints is controlled by role-based authorization. Your client application must request an access token, and the issued JWT will contain your assigned roles.

  • Contract-specific roles ({contractId}_R, {contractId}_W) provide read and write access to resources under a specific contract.
  • The DPS_SUBMISSION_HANDLER role is required for internal status management operations.

Error Handling

The API uses standard HTTP status codes to indicate the success or failure of requests:

  • 200 OK - Request was successful
  • 201 Created - Resource was successfully created
  • 400 Bad Request - Invalid request parameters or payload
  • 401 Unauthorized - Authentication failed or token is missing/invalid
  • 403 Forbidden - Access token is valid but lacks required roles
  • 404 Not Found - Resource not found
  • 409 Conflict - Duplicate/concurrent request
  • 422 Unprocessable Entity – Resource exists but is not preserved / not ready
  • 500 Internal Server Error - An unexpected error occurred on the server
  • 502 Bad Gateway - A service the API depends on failed

Error responses follow RFC 9457 Problem Details for HTTP APIs and are returned with the media type application/problem+json:

{
  "type": "https://digitalpreservation.no/problems/submission-not-found",
  "title": "Submission Not Found",
  "status": 404,
  "detail": "Submission with ID 7mVKGHwOA6w2Ot8feF9fIU was not found",
  "instance": "/v1/contracts/4eaf/submissions/7mVKGHwOA6w2Ot8feF9fIU",
  "timestamp": "2025-10-14T10:03:00.123Z",
  "errorCategory": "NOT_FOUND"
}
FieldDescription
typeURI that identifies the problem type
titleShort, human-readable summary of the problem type
statusHTTP status code
detailHuman-readable explanation of this occurrence of the problem
instanceURI that identifies this occurrence of the problem
timestampWhen the error occurred (ISO 8601)
errorCategoryOne of NOT_FOUND, CONFLICT, VALIDATION_ERROR, BUSINESS_RULE_VIOLATION, AUTHORIZATION_ERROR, INTERNAL_ERROR, EXTERNAL_SERVICE_ERROR
validationErrorsOnly for validation errors: an object that maps each invalid field to a list of messages
currentStatusOnly for errors caused by the resource’s state: the current status of the resource

Submissions

Endpoints for submitting content. For a step-by-step walkthrough, see Submit content for preservation. All paths are relative to https://api.nb.no/dps-submission.

Submission endpoints

MethodPathDescription
POST/v1/contracts/{contractId}/submissionsCreate submission
GET/v1/contracts/{contractId}/submissionsList submissions for the contract
GET/v1/contracts/{contractId}/submissions/{submissionId}Fetch submission
DELETE/v1/contracts/{contractId}/submissions/{submissionId}Delete submission
POST/v1/contracts/{contractId}/submissions/{submissionId}/filesRegister file and get upload URL
GET/v1/contracts/{contractId}/submissions/{submissionId}/files/{fileId}Fetch file
DELETE/v1/contracts/{contractId}/submissions/{submissionId}/files/{fileId}Delete file
POST/v1/contracts/{contractId}/submissions/{submissionId}/finalizeFinalize submission
POST/v1/contracts/{contractId}/submissions/{submissionId}/eventsSubmit event, see Preservation events
GET/v1/contracts/{contractId}/submissions/{submissionId}/eventsFetch events
  • List submissions: Ordered by creation date and paginated with page (zero-based, default 0) and size (default 10). Filter with objectId and/or archiveId (exact match; multiple filters are combined with AND). If nothing matches, the response is an empty page.
  • Fetch submission: Use the include query parameter to add related data to the response: FILES (registered files), DESCRIPTIVE_METADATA (the metadata from the request) and EVENTS (preservation events), e.g. ?include=FILES&include=EVENTS.
  • Delete submission: Only allowed while the submission has the status REGISTERED and has no registered files. Delete the files first; a submission that still has files returns 409 Conflict.
  • Delete file: Only allowed while the submission has the status REGISTERED; otherwise the API returns 409 Conflict. By default only the file registration is deleted. Add deleteFromS3=true to also delete the uploaded file from S3.
  • Finalize submission: The submission must have at least one registered, non-empty file, and every registered file must be in S3; otherwise the API returns 422 Unprocessable Entity. A submission can only be finalized once; a second call returns 409 Conflict.
  • Submit event: Only allowed while the submission has the status REGISTERED. A successful request returns 204 No Content.

Submission request fields

Create submission

FieldTypeRequiredDescription
objectIdstringYesThe client’s identifier for the submission object – typically an ID from the source system (e.g. a database ID or URN fragment). Links DPS events such as webhooks back to the object in your system. Must be unique per contract (the combination of contractId + objectId must be unique). Cannot be changed after the submission has been delivered to DPS. Must be 3–200 characters and may only contain a-z, A-Z, 0-9, - and _.
priorityintegerNoPriority for processing in the queue. Valid values: 1 (highest) to 100 (lowest). Default is 50 (normal priority), which is what should be used in most cases. Values above 50 indicate lower priority and are used e.g. for re-archiving.
metadataobjectYesDescriptive metadata about the content. See Metadata Requirements for the available elements.

Register file

FieldTypeRequiredDescription
filePathstringYesRelative path of the file within the submission. Must be unique within the submission, at most 900 characters, may only contain a-z, A-Z, 0-9, -, _, ., (, ) and /, and cannot start with / or ..
checksumstringYesMD5 checksum of the file as 32 lowercase hexadecimal characters.
isPackagedbooleanNoWhether the file is packaged temporarily for transfer only (for example as TAR or ZIP). Such files are unpacked by the preservation flow. Default is false.

The response contains the fileId, the s3ObjectKey, and an uploadUrl for uploading the file content.

Submission limits

LimitValue
Maximum file size for upload through a pre-signed URL5 GiB per file
Validity of a pre-signed upload URL (uploadUrl)Approximately 1 hour
Checksum algorithmMD5

The file size limit is due to constraints in S3 pre-signed URLs. Larger files can be uploaded directly to S3 using the s3ObjectKey; this is primarily intended for internal use at the National Library.

Submission status lifecycle

A submission progresses through the following statuses:

  1. REGISTERED - Initial state when a submission is created
  2. UPLOAD_COMPLETED - All files have been uploaded and the submission has been finalized
  3. PROCESSING - The submission is being transferred to, validated and archived in the preservation system
  4. PRESERVED - The submission has been successfully preserved
  5. REJECTED - The submission has been rejected due to validation errors or other issues

Changes to PRESERVED and REJECTED are also sent as webhook messages (submission.preserved and submission.rejected).

Disseminations

Endpoints for requesting delivery of preserved content. For a step-by-step walkthrough, see Request preserved content. All paths are relative to https://api.nb.no/dps-submission.

Dissemination endpoints

MethodPathDescription
POST/v1/disseminationsCreate new dissemination
GET/v1/disseminations/{disseminationId}Fetch dissemination
  • Create dissemination: The API checks that the client is authorized, that the package exists and is preserved, and that the request is not a duplicate. If a dissemination is already in progress, the API returns 409 Conflict. Preparation then runs in the background, and the result is delivered through the dissemination.delivered webhook.

Dissemination request fields

FieldTypeRequiredDescription
archiveIdstringYesUnique identifier for the archive object in DPS to be disseminated. Received via the submission.preserved webhook.
priorityintegerNoPriority for processing in the queue. Valid values: 1 (highest) to 100 (lowest). Default is 50 (normal priority).

Dissemination status lifecycle

Preliminary statuses (may be extended):

  1. RECEIVED – Received
  2. QUEUED – Placed in queue
  3. DOWNLOADING_FROM_REPOSITORY – Downloading from repository
  4. FIXITY_CHECK – Integrity verification
  5. UPLOADING_TO_S3 – Uploading to S3
  6. DISSEMINATED – Delivered
  7. FAILED – Failed
  8. REJECTED – Rejected

The download links in the dissemination.delivered webhook expire at the time given in expirationDate. Each new dissemination gets new links.

Webhooks

Messages that DPS sends to your webhook endpoint. For how to set up and verify an endpoint, see Receive notifications. DPS uses Standard Webhooks for format and signing.

Webhook delivery

PropertyRequirement
ProtocolHTTPS (TLS 1.2 or higher)
MethodPOST
Content-Typeapplication/json
AuthenticationHMAC signature in header (webhook-signature)
CertificateValid TLS certificate required
StandardStandard Webhooks
Response200 OK or 204 No Content within 10 seconds
RetriesYes (exponential backoff, up to 5 days total)
ConfigurationHandled manually by NB

Webhook headers

All messages are sent with the following headers:

webhook-id: <unique id>
webhook-timestamp: <unix seconds>
webhook-signature: v1,<base64-signature>
HeaderDescription
webhook-idUnique ID for the message (same ID used for retries – use as deduplication key)
webhook-timestampUNIX time in seconds; use a time window (±5–10 min) to reject old messages
webhook-signatureHMAC-SHA256 of id + "." + timestamp + "." + raw request body, signed with shared key

Tip: Always verify the signature against the raw request body, not a re-serialized JSON structure.

Webhook message structure

All webhook messages follow the same structure (“envelope”):

{
  "type": "<event type>",
  "timestamp": "2025-09-10T00:08:11.407000+02:00",
  "data": {}
}
FieldTypeDescription
typestringEvent type (see below)
timestampstringTimestamp when the event occurred, in RFC3339/ISO 8601 format with explicit offset or UTC (Z)
dataobjectEvent-specific fields

Webhook events

DPS currently supports the following events.

submission.preserved

Webhook sent when a SIP submission is preserved and stored permanently.

Example

{
  "type": "submission.preserved",
  "timestamp": "2025-09-10T00:08:11.407000+02:00",
  "data": {
    "contractId": "ef23",
    "submissionId": "8Z7x1T9rN0Xc2B5Yq4L3zP",
    "archiveId": "019e404e-60ab-7457-b7c9-94c453242c69"
  }
}
FieldTypeDescription
contractIdstringIdentifier for the contract or agreement the submission is linked to
submissionIdstringUnique identifier for the SIP submission
archiveIdstringUnique identifier for the preserved archive object in DPS. Assigned when the submission is archived. Use this ID to reference the object in future requests, e.g. when ordering a dissemination.

submission.rejected

Webhook sent when a SIP submission is rejected during validation or processing.

Example

{
  "type": "submission.rejected",
  "timestamp": "2025-09-10T00:08:11.407000+02:00",
  "data": {
    "contractId": "ef23",
    "submissionId": "8Z7x1T9rN0Xc2B5Yq4L3zP",
    "archiveId": "019e404e-60ab-7457-b7c9-94c453242c69",
    "reasons": [
      {
        "code": "METADATA_SCHEMA_INVALID",
        "message": "Descriptive metadata did not validate against the required profile."
      },
      {
        "code": "FILE_CHECKSUM_MISMATCH",
        "message": "Checksum mismatch.",
        "filePath": "objects/issue_1942_05.pdf"
      }
    ]
  }
}
FieldTypeDescription
contractIdstringIdentifier for the contract or agreement the submission is linked to
submissionIdstringUnique identifier for the SIP submission
archiveIdstringUnique identifier for the archive object in DPS
reasonsarrayList of reasons for rejection. At least one entry.
→ codestringMachine-readable error code identifying the type of error. Stable over time
→ messagestringHuman-readable description of the error
→ filePathstringRelative path to the file the error pertains to, if the error can be linked to a specific file. If this field is missing, the error applies to the entire package.

dissemination.delivered

Webhook sent when a delivery package (DIP) is made available for download.

Example

{
  "type": "dissemination.delivered",
  "timestamp": "2025-10-02T09:18:01.160+02:00",
  "data": {
    "archiveId": "019e404e-60ab-7457-b7c9-94c453242c69",
    "disseminationId": "5MfwdzCjkYW4c79MYorXy9",
    "objectId": "digifoto_ae0690eb-22bf-4996-a6a0-9273b7cd9256",
    "clientId": "kulturit",
    "contractId": "2d17",
    "sumSizeInBytes": "1",
    "files": [
      {
        "downloadURL": "https://minio.dev.nb.no/submission-service-stage/dissemination/5MfwdzCjkYW4c79MYorXy9/68d3a838a0be2b1d75eeef75/primary_20250325.tar?...",
        "filename": "primary_20250325.tar",
        "filesize": 3481600,
        "expirationDate": "2025-10-03T09:18:01.023897767+02:00",
        "checksum": "ae958c69059974c63980035882d2178c",
        "checksumAlgorithm": "MD5"
      }
    ]
  }
}
FieldTypeDescription
archiveIdstringUnique identifier for the preserved archive object in DPS. Same ID as in submission.preserved. Use it to link the dissemination back to the original submission.
disseminationIdstringUnique identifier for the delivery package
objectIdstringObject identifier from the depositor – same value as provided when creating the submission
clientIdstringRecipient
contractIdstringIdentifier for the contract or agreement the delivery package is linked to
sumSizeInBytesintegerTotal size in bytes
filesarrayList of files in the package
→ downloadURLuriPre-signed download URL
→ filenamestringFile name
→ filesizeintegerFile size in bytes
→ expirationDatedate-timeExpiration date for the URL
→ checksumstringChecksum
→ checksumAlgorithmenumAlgorithm used for checksum

Support

For API support, please contact the Digital Preservation team.

API Reference

For complete API details including endpoints, parameters, request/response schemas, and status codes, please refer to the Swagger documentation.

Last updated on - Github commit history ↗