I am a Zivver admin
Configure and manage Zivver
Export data via API
Introduction
Export all Zivver messages that were sent and received by your organization for archiving purposes. This method allows you to export messages via the Zivver API.
This export method is recommended for:
- Organizations with a large number of messages
- Exporting data for a subset of users
- Situations where the ZIP export is not suitable
For smaller organizations with a limited number of messages, we recommend using the Zivver WebApp method to export all messages in a ZIP file via a Zivver message.
API overview
The sections below provide an overview of the API endpoints used for exporting data. After the API is enabled for your organization, you can use these endpoints to manage your exports via a script or an API client (e.g. Postman). All endpoints require authentication via an API token, which you generate in the Zivver admin portal.
The base URL for all endpoints is https://app.zivver.com/api, and all requests use the GET method.
/exports/mailbox
With this endpoint, you can retrieve a list of all mailboxes in your organization. This endpoint is the starting point for the export process, as you need to know which mailboxes to export.
Include the following headers in your request:
Authorization: Bearer {API_TOKEN}Accept: application/json
/exports/mailbox/{email_address}
Use this endpoint to retrieve a list of months for which there are conversations in a specific mailbox. This allows you to identify which months have data available for export.
Include the following headers in your request:
Authorization: Bearer {API_TOKEN}Accept: application/json
/exports/mailbox/{email_address}/{year_month}
This endpoint retrieves a list of conversations in a specific month for a specific mailbox. This helps you identify which conversations you want to export.
Include the following headers in your request:
Authorization: Bearer {API_TOKEN}Accept: application/json
Only this list endpoint is paginated. The other list endpoints return all items in one response. A response contains up to 100 conversations in items. If there are more conversations, the response also contains a cursor value:
- To retrieve the next page, pass the
cursorvalue unchanged as thecursorquery parameter in the next request. The value is opaque, so do not try to interpret or change it. - When a response contains no
cursorvalue, you have reached the last page. - The page size is fixed at 100 conversations and cannot be changed.
- An invalid
cursorvalue returns status400withInvalid cursor parameter. - A month in a valid
yyyyMMformat without conversations returns{"items": []}.
For example, the first request returns 100 conversations and a cursor value. The second request, with that value, returns the remaining conversations and no cursor, so it is the last page:
GET /exports/mailbox/jan@example.com/202603
GET /exports/mailbox/jan@example.com/202603?cursor=7f3e9c2a-...
/exports/mailbox/{email_address}/{year_month}/{conversationNameKey}
Use this endpoint to retrieve a list of messages in a conversation for a specific month and mailbox. This allows you to see the individual messages that are part of the conversation.
Include the following headers in your request:
Authorization: Bearer {API_TOKEN}Accept: application/json
/exports/mailbox/{email_address}/{year_month}/{conversationNameKey}/{messageNameKey}
This endpoint allows you to download a specific message in a conversation for a specific month and mailbox. The message is downloaded in EML format, which can be opened with email clients or imported into other systems.
Include the following headers in your request:
Authorization: Bearer {API_TOKEN}Accept: application/octet-stream
Proof of Delivery reports
You can also export the Proof of Delivery report of each message as a PDF. The report shows when a message was sent, who it was sent to, and what happened to it per recipient, such as whether it was delivered and opened. Use the report to archive evidence of delivery together with the message, for example in a document management system.
Requirements for Proof of Delivery reports
Before you begin, make sure you meet the following requirements:
- API access is enabled for your organization (see API overview)
- The Proof of Delivery report is enabled for your organization. This is separate from the API endpoints, so create a support ticket to have it enabled.
- The API token has the
export:readpermission. All export endpoints require this permission, and the/podendpoint needs no extra token permission.
/exports/mailbox/{email_address}/{year_month}/{conversationNameKey}/{messageNameKey}/pod
This endpoint downloads the Proof of Delivery report of a specific message as a PDF file. It uses the same path as the endpoint that downloads the message, followed by /pod. Use the values returned by the other export endpoints for each part of the path:
| Path parameter | Value | Returned by |
|---|---|---|
email_address | Mailbox to export | /exports/mailbox |
year_month | Month in yyyyMM format | /exports/mailbox/{email_address} |
conversationNameKey | Conversation name | /exports/mailbox/{email_address}/{year_month} |
messageNameKey | Message name, for example 0001_20260115T093000Z.eml | /exports/mailbox/{email_address}/{year_month}/{conversationNameKey} |
Include the following headers in your request:
Authorization: Bearer {API_TOKEN}Accept: application/pdfAccept-Language: {language}(optional, see Report language)
Only the message number at the start of messageNameKey is used to find the message: at least four digits followed by an underscore, such as 0001_. The rest of the name can change after you list it, for example when a message is revoked. A message name that you stored earlier remains valid.
Report language
The report language follows the Accept-Language header. The following languages are supported:
| Language | Code |
|---|---|
| Dutch | nl |
| English | en |
| French | fr |
| German | de |
| Italian | it |
| Polish | pl |
| Portuguese | pt |
| Spanish | es |
| Swedish | sv |
Regional variants are supported, for example de-DE returns a German report. If the header is missing or does not contain a supported language, the report is in English.
Delivery and receipt reports
The type of report depends on the role of the mailbox owner for the requested message. You cannot choose the type yourself.
| Mailbox owner | Report | Contents |
|---|---|---|
| Sent the message | Delivery report (delivery_report) | Message details, and for every recipient the delivery status, whether the message was opened, and a timeline of events |
| Received the message | Receipt report (receipt_report) | Message details, and the same information for the mailbox owner only. Other recipients are not included. |
The message details in both reports are the message ID, sender, recipients, subject, attachment names and time of sending. The header of the report shows the mailbox address and the time the report was generated.
The type is determined per message. In a conversation where the mailbox owner sent the first message and received a reply, the first message has a delivery report and the reply has a receipt report.
To prove that a message was sent and delivered, request the report from the mailbox of the sender. To prove that a message was received, request the report from the mailbox of the recipient.
Response
A successful request returns status 200 with the report as a PDF file (Content-Type: application/pdf). The file name is included in the Content-Disposition header:
Content-Disposition: attachment; filename="delivery_report_0001_20260115T093000Z_Quarterly_report.pdf"
The file name consists of:
- The report type:
delivery_reportorreceipt_report(see Delivery and receipt reports) - The message name without
.emland without marks such asREVOKED - The subject of the conversation. Characters other than
A-Z,a-z,0-9,.,_and-are replaced by_, and the subject is shortened to 30 characters. The subject is left out if no usable characters remain.
The report is generated at the moment you request it and is based on the delivery information available at that time. If you request the same report again later, it can contain new events, such as a recipient who opened the message in the meantime. Store the downloaded PDF if you need a fixed copy.
Special cases
Revoked and deleted messages
The report remains available after a message is revoked or deleted.
- When the mailbox owner no longer has access to the message, the message name in the list contains
REVOKEDorDELETED, for example0001_REVOKED_20260115T093000Z.eml. Messages that the mailbox owner revoked or deleted themselves keep their original name. - Both the new name and the name you stored before return the same report, with the same file name.
- The report still lists the names of the attachments, even though the message itself can no longer be exported.
Forwarded messages
When a message is forwarded, a copy of the original message is added to the new conversation. In the list of messages, the copy is marked with FORWARDED, for example 0001_FORWARDED_20260114T080000Z.eml.
- A forwarded copy has no report of its own. A request returns status
404withNo delivery events are recorded for this message. - To get the report of the forward itself, request the message without the
FORWARDEDmark, for example0002_20260115T093000Z.eml. - To get the report of the original message, request it in the conversation in which it was originally sent.
Inaccessible conversations
A conversation is inaccessible when its content can no longer be decrypted for the mailbox owner, for example after a password reset without a recovery code. The conversation name ends with _INACCESSIBLE and the list of messages is empty. A request for a report in this conversation returns status 404 with Conversation not found.
Errors
If a request fails, the response body contains a short error message in plain text. The Content-Type header of an error response is still application/pdf, so always check the status code before you process the response as a PDF file.
| Status | Message | Cause |
|---|---|---|
400 | Invalid email address: {email_address} | The email address is not valid |
401 | {"reason":"INVALID_CREDENTIALS"} | The API token is missing or invalid |
403 | Export API is not enabled | The export API is not enabled for your organization |
403 | Proof of Delivery is not enabled | The Proof of Delivery report is not enabled for your organization |
404 | Email address not found: {email_address} | The mailbox does not exist in your organization |
404 | Year month not found: {year_month} | The month is not in yyyyMM format |
404 | Conversation not found: {conversationNameKey} | The conversation does not exist in this mailbox and month, or it is inaccessible |
404 | Message not found: {messageNameKey} | The message name does not start with a message number, or the conversation has no message with this number |
404 | No delivery events are recorded for this message | No delivery information exists for this message, for example a forwarded copy |
500 | Internal server error | An unexpected error occurred. You can retry the request. |
503 | - | The export service is busy. Retry after the number of seconds in the Retry-After header. |
Export reports with a script
To export the reports of all messages, follow the same steps as for the message export (see API overview) and request the report of each message with the /pod endpoint.
Keep the following in mind:
- Status
404withNo delivery events are recorded for this messageis expected for forwarded messages. Treat it as “no report available” instead of a failed download. - This endpoint shares its capacity with the message download endpoint. When you receive status
503, wait for the number of seconds in theRetry-Afterheader before you retry.
Python export script
To simplify the export process, we provide a Python script that automates the API calls and handles the export logic. The script supports features such as filtering by email address or domain, exporting Proof of Delivery reports, resuming interrupted exports, and retrying failed downloads. The script is included in the export package that you can download from the link below. It is designed to be run from the command line and can be configured using a configuration file or command line arguments.
Requirements
Before you begin, make sure you meet the following requirements:
- Python 3.9 or higher is installed
- Poetry is installed (recommended)
- API access is enabled for your organization
Download
Download the export package, which includes the Python script, a README file, and a sample configuration file:
Configuration
API token (required)
Set your API token as an environment variable:
export ZIVVER_API_TOKEN="your-token-here"
ZIVVER_API_TOKEN environment variable and should not be stored in the configuration file.Option 1: Configuration file
Use a configuration file if you want to manage all settings in one place.
Create a config.json file (see the example included in the download):
{
"api_token": "Please use environment variable ZIVVER_API_TOKEN to set your API token.",
"output_dir": "./exports",
"allowed_emails": ["user1@example.com"],
"allowed_domains": ["example.com"],
"exclude_emails": ["noreply@example.com"],
"max_workers": 10,
"debug": false,
"content": ["eml", "pod"],
"language": "nl"
}
Use content to choose what the script downloads for each message: eml for the message, pod for its Proof of Delivery report, or eml,pod for both. The default is ["eml"]. Use language to set the language of the reports. See Export reports with the Python script.
This approach is recommended when:
- You want a reusable configuration
- You are running multiple exports with the same settings
Option 2: Command line arguments
Use command line arguments if you prefer to quickly run one-off exports without creating a config file.
organization-data-export \
--output-dir ./my-exports \
--allowed-emails user1@example.com,user2@example.com \
--allowed-domains example.com
This approach is useful when:
- You want to override specific settings
- You are testing or running ad-hoc exports
allowed_emails and allowed_domains, emails matching either condition are included (OR logic).Installation
Using Poetry (recommended)
Use this method if you want to run the script in a managed Python environment with all dependencies handled automatically.
poetry install
poetry run organization-data-export --help
This will:
- Install all required dependencies
- Allow you to run the script using
poetry run
Using a built package (wheel)
Use this method if you prefer a standard installation that makes the command globally available.
poetry build
pip install dist/organization_data_export-*.whl
organization-data-export --help
This will:
- Build the package
- Install it so you can run
organization-data-exportdirectly
Usage
Full export
Use this command to export all available data using a configuration file:
organization-data-export export --config config.json
Export specific email addresses
Use this when you only want to export data for selected users:
organization-data-export export --allowed-emails user1@example.com,user2@example.com
Export specific domains
Use this when you want to export all users within one or more domains:
organization-data-export export --allowed-domains example.com
Export Proof of Delivery reports
Use this when you want to export the Proof of Delivery report of each message, with or without the messages:
organization-data-export export --content eml,pod --language nl
See Export reports with the Python script for details.
Resume an interrupted export
If an export was interrupted (for example due to a network issue or manual stop), you can resume it:
organization-data-export export --resume
This will continue from where the previous run stopped without re-downloading existing data.
Retry failed downloads
If some files failed to download, you can retry only those files:
organization-data-export retry-failed
The log records for each failed file whether it is a message or a Proof of Delivery report, and the language of a report, so the script retries the same type of file in the same language. Use --language to retry the reports in another language:
organization-data-export retry-failed --language nl
retry-failed only retries single files. When the script can’t list the conversations of a month, or the messages of a conversation, it continues with the rest and doesn’t mark that month as complete. The summary shows how many listings failed. Run the same export again with --resume to finish those months. A conversation that was deleted during the export isn’t a failed listing: the script skips it, still marks the month as complete, and shows the number of deleted conversations in the summary. When a listing fails with an error that retrying doesn’t solve, such as for a mailbox that was deleted during the export, the summary lists it separately with the error. --resume only finishes those months once the cause of the error is solved, and skips a mailbox that no longer exists.
Output structure
Exported files are stored in the following structure:
exports
└── user@example.com
└── 202601
└── conversation-id
├── 0001_20260115T093000Z.eml
├── 0001_20260115T093000Z.pod.pdf
├── 0002_20260116T101500Z.eml
└── 0002_20260116T101500Z.pod.pdf
The .pod.pdf files are only downloaded when content includes pod.
Export reports with the Python script
The script can also export the Proof of Delivery report of each message. Proof of Delivery must be enabled for your organization. Use --content to choose what to download for each message, and --language to set the language of the reports:
| Command | Result |
|---|---|
organization-data-export export --content eml,pod --language nl | Messages and their reports in Dutch |
organization-data-export export --content pod --language de-DE --allowed-emails user@example.com | Only the reports of one mailbox, in German |
organization-data-export export --content pod | Only the reports, in English |
Without --content, the script exports only the messages, as before. Existing commands and configuration files keep working without changes.
You can also set content and language in the configuration file. The script stops before it starts when the language is not supported.
The script saves each report next to its message, with .pod.pdf instead of .eml. For example, the report of 0001_20260115T093000Z.eml is saved as 0001_20260115T093000Z.pod.pdf. The script also does the following:
- It skips forwarded copies, which have no report, and counts them separately in the summary. They are not logged as failed downloads.
- It stops with an error when the API refuses a request with status
403, for example when Proof of Delivery or the export API is not enabled for your organization. After it is enabled, run the same command again with--resumeto continue. - It shares the rate limit of the API token between its concurrent downloads, and slows down before the limit runs out. It waits for the number of seconds in the
Retry-Afterheader when the API asks it to. - It does not download a report again when it exists, also not when the message was revoked or deleted in the meantime and has another name.
- It adds the reports to an existing export of messages without downloading the messages again.
Reports that exist are not downloaded again, also not when you use another --language. To download them again, for example to include new events, use a new output directory.
Important considerations
- Progress is not linear: some mailboxes complete quickly, others take much longer
- The script processes one month at a time per mailbox
- The export may appear to be stuck, but is still running
- Rate limiting is handled automatically
Features
- Resumable exports using
--resume - Flexible filtering by email address and domain
- Proof of Delivery reports in the language you choose
- Concurrent downloads (configurable)
- Automatic retry for failed downloads
- Logging of failed downloads
- Secure authentication via environment variables
Limitations
- API access must be enabled by Zivver for security purposes
- If you want to use Zivver’s export script, Python installation is required
- The API token must be set again for each session (unless configured permanently)
FAQ
How to enable the API endpoints?
The API endpoints are disabled by default. Create a support ticket to have them enabled for your organization.What to do if the script stops midway?
Run the same command again and use the--resume flag to continue where it left off.Some files failed to download. How to retry?
Use the following command to retry only the failed downloads:organization-data-export retry-failed
How to export data for specific users only?
Useallowed_emails to include specific users, or allowed_domains to include entire domains. You can also use exclude_emails to skip specific addresses.