Package Releases API

The Package Releases API allows you to request the release of one or more packages from eVault, download the approved exports, and acknowledge receipt.

Overview

A Package Release is the integration-facing way to paper out a package: you request a release, the Concord operations team reviews the request, and approval produces a downloadable export of the authoritative copy. Custody of the authoritative copy transfers out of eVault on the first download.

Key concepts: A Release Request is your request to release a package, reviewed by the operations team. An Export is the ZIP artifact produced when a request is approved, identified by a certificate number in format PO-{date}-{hex} (for example, PO-20260115-A7B3C9D1). Custody Transfer means the first download of the export permanently transfers custody; the package remains in eVault as a receipt copy only. Acknowledgement is your confirmation that the export was downloaded and verified, after which the export file is destroyed.

Release lifecycle

The release request (status) and its export (export.status) have separate lifecycles, and both use the value Pending at different times: a request that is Pending is awaiting review, while an approved request briefly carries an export that is Pending generation (status: "Approved", export.status: "Pending").

  1. Request Pending: The release request is awaiting review by the operations team.
  2. Request Approved: The request was approved and export generation begins; request Declined: the request was rejected with a documented reason.
  3. Export Pending → Processing → Ready: The export ZIP is generated and becomes downloadable.
  4. Export Ready → Downloaded: The first download transfers custody of the authoritative copy.
  5. Export Acknowledged: You confirm verified receipt; the export file is destroyed.

The export can be re-downloaded between the first download and acknowledgement. Exports expire 7 days after they become ready; expired exports are destroyed whether or not they were downloaded.

Required scopes

ScopeDescription
package-releases:readView release requests and export status
package-releases:writeRequest, export, and acknowledge releases
package-releasesFull access (read + write)

Request Package Releases

Submit release requests for one or more packages. Each package must be an active authoritative copy with no paper out in progress, no pending transfers, and no pending transfer batch. Eligibility is evaluated per package: ineligible packages are skipped with a documented reason and do not fail the batch.

POST /api/package-releases

Required scope: package-releases:write.

{
  "vaultPackageIds": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "4fa85f64-5717-4562-b3fc-2c963f66afa7"
  ]
}

Request body

FieldTypeRequiredDescription
vaultPackageIdsguid[]YesPackage IDs to request release for. Max 25. Duplicates are ignored.

Response (202 Accepted)

{
  "createdCount": 1,
  "skippedCount": 1,
  "results": [
    {
      "vaultPackageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "releaseRequest": {
        "id": "8fa85f64-5717-4562-b3fc-2c963f66afa6",
        "vaultPackageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "packageName": "Loan 100234",
        "status": "Pending",
        "requestedByName": "My Integration Key",
        "declineReason": null,
        "reviewedAt": null,
        "createdAt": "2026-01-15T10:30:00Z",
        "export": null
      },
      "skipReason": null
    },
    {
      "vaultPackageId": "4fa85f64-5717-4562-b3fc-2c963f66afa7",
      "releaseRequest": null,
      "skipReason": "A release request is already pending for this package."
    }
  ]
}

releaseRequest is set for each created request — use its id to poll for approval. skipReason documents why a package was skipped (not found, not an active authoritative copy, paper out in progress, pending transfers, or a release request already pending).

Error responses: 400 — No package IDs provided, more than 25 package IDs provided, or the vault is archived; 404 — Vault not found.

Get Package Release

Retrieve a release request, including the export status once approved. Poll this endpoint after approval to learn when the export is ready.

GET /api/package-releases/{releaseRequestId}

Required scope: package-releases:read. Path parameter releaseRequestId (guid, required).

Response (200 OK)

{
  "id": "8fa85f64-5717-4562-b3fc-2c963f66afa6",
  "vaultPackageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "packageName": "Loan 100234",
  "status": "Approved",
  "requestedByName": "My Integration Key",
  "declineReason": null,
  "reviewedAt": "2026-01-16T09:00:00Z",
  "createdAt": "2026-01-15T10:30:00Z",
  "export": {
    "status": "Ready",
    "certificateNumber": "PO-20260116-A7B3C9D1",
    "fileSizeBytes": 1048576,
    "checksum": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "expiresAt": "2026-01-23T09:05:00Z",
    "downloadedAt": null,
    "acknowledgedAt": null,
    "canExport": true
  }
}

The export object is null until the request is approved. canExport is true when the export can currently be downloaded. If the request was declined, status is Declined and declineReason documents the decision.

Error responses: 404 — Release request not found.

List Package Releases

List release requests for the vault, optionally filtered by status.

GET /api/package-releases

Required scope: package-releases:read.

Query parameters

ParameterTypeRequiredDescription
pageintNoPage number (default 1)
pageSizeintNoItems per page (default 20)
statusstringNoFilter: Pending, Approved, or Declined (case-insensitive)

Response (200 OK)

{
  "data": [
    {
      "id": "8fa85f64-5717-4562-b3fc-2c963f66afa6",
      "vaultPackageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "packageName": "Loan 100234",
      "status": "Pending",
      "requestedByName": "My Integration Key",
      "declineReason": null,
      "reviewedAt": null,
      "createdAt": "2026-01-15T10:30:00Z",
      "export": null
    }
  ],
  "metadata": { "page": 1, "pageSize": 20, "totalCount": 1 }
}

Export Package Release

Download the approved release export as a ZIP file.

POST /api/package-releases/{releaseRequestId}/export

Required scope: package-releases:write.

This is a POST (not GET) because the first download permanently transfers custody of the authoritative copy — an irreversible state change. The response body is the ZIP file. Keys with only package-releases:read receive a 403.

Warning: The first download permanently transfers custody of the authoritative copy out of eVault. The package remains visible in eVault as a receipt copy only. The export stays downloadable until you acknowledge receipt or the download window expires.

Path parameters: releaseRequestId (guid, required).

Request body: None.

Response (200 OK): Returns the ZIP file with the following headers:

HeaderDescription
Content-DispositionAttachment with generated filename
X-Paper-Out-CertificateThe export certificate number
X-Paper-Out-ChecksumSHA-256 checksum for integrity verification

Best practice: Verify the downloaded file against X-Paper-Out-Checksum before acknowledging.

Error responses: 400 — Request not approved, request declined, export still generating, export expired, or export already acknowledged and destroyed; 404 — Release request not found.

Acknowledge Package Release

Confirm that the exported package was downloaded and its contents verified. The export file is destroyed in eVault; the receipt copy of the package remains for records.

POST /api/package-releases/{releaseRequestId}/acknowledge

Required scope: package-releases:write. Path parameter releaseRequestId (guid, required). Request body: none.

Response (200 OK): Returns the updated release request. export.acknowledgedAt is set and export.canExport becomes false.

Error responses: 400 — Export not yet downloaded, or already acknowledged; 404 — Release request not found.

Typical integration flow

  1. POST /api/package-releases — request releases for one or more packages.
  2. For each created request, poll GET /api/package-releases/{id} until status is Approved and export.status is Ready (or handle a Declined status using declineReason).
  3. POST /api/package-releases/{id}/export — download the ZIP (custody transfers on first download).
  4. Verify the ZIP contents against X-Paper-Out-Checksum.
  5. POST /api/package-releases/{id}/acknowledge — confirm receipt so the export is destroyed.

Note: Exports expire 7 days after becoming ready. Download and acknowledge promptly; an expired export requires a new release request.

Next: Appendix.