API Reference
Programmatically submit PDF reports, receive a signed download URL and single-use access
password, and let recipients retrieve the document via browser or curl.
All endpoints require mutual TLS 1.3 and an API key. EU residency — data never leaves Frankfurt
(PROD-EU-2).
Overview
The API has three verbs: upload a document, download a document, and revoke a link. A successful upload returns a one-time URL and a one-time password. We hash the password with Argon2id and discard the plaintext immediately — it cannot be retrieved later. If a recipient loses the password, generate a new one with the revoke-and-reissue endpoint.
https://api.secdl.portal42.cc/v2application/pdf uploads · application/json responsesAuthentication
Every request must include an Authorization: Bearer <key>
header. API keys are scoped per issuing organization and rotate every 180 days.
# Store your key securely — environment variable or secret manager export SECDL_API_KEY="sk_live_7fA3…Bv21"
Upload a document
Upload a PDF and set access controls. The response includes a download URL and a one-time password — surface both to the recipient (typically in separate channels: URL via email, password via SMS or chat).
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
| file | binary | yes | The PDF payload. Multipart form field. |
| title | string | yes | Human-readable report title shown on the download page. |
| recipient_name | string | yes | Displayed on the download page as confirmation of intended recipient. |
| recipient_email | string | yes | Used only for password reissue requests; never receives the initial password. |
| expires_in | duration | no | ISO-8601 duration. Default P7D (7 days). Max P90D. |
| max_downloads | integer | no | Default 1. Set higher for multi-retrieval links. |
| metadata | object | no | Free-form key/value pairs mirrored back on retrieval. |
Example request
curl -X POST https://api.secdl.portal42.cc/v2/documents \ -H "Authorization: Bearer $SECDL_API_KEY" \ -F file=@./q1-audit-report.pdf;type=application/pdf \ -F title="Q1 2026 Financial Statements & Audit Report" \ -F recipient_name="Frauke Lindqvist" \ -F recipient_email="f.lindqvist@nordhafen.ag" \ -F expires_in="PT24H" \ -F max_downloads="1"
Example response 201 Created
{ "id": "SDL-2026-0423-AC4F", "download_url": "https://secdl.portal42.cc/d/SDL-2026-0423-AC4F", "password": "R4TA-9XPM-KQV7-B2LD-ZN8E", // shown ONCE — not retrievable later "sha256": "e3b0c44298fc1c149afbf4c8996fb924…", "size_bytes": 5054234, "expires_at": "2026-04-24T11:00:00Z", "max_downloads": 1, "created_at": "2026-04-23T11:00:00Z" }
Download via curl
The download endpoint accepts the access password via HTTP Basic auth — the URL path is the document ID, the username is ignored, and the password is the one-time access password. This makes it trivial for recipients to retrieve the PDF from a terminal or CI pipeline.
# Option A — Basic auth (password only) curl -u ":R4TA-9XPM-KQV7-B2LD-ZN8E" \ -o q1-audit-report.pdf \ https://secdl.portal42.cc/d/SDL-2026-0423-AC4F/content # Option B — X-Access-Password header curl -H "X-Access-Password: R4TA-9XPM-KQV7-B2LD-ZN8E" \ -o q1-audit-report.pdf \ https://secdl.portal42.cc/d/SDL-2026-0423-AC4F/content # Verify the checksum shasum -a 256 q1-audit-report.pdf # e3b0c44298fc1c149afbf4c8996fb924… q1-audit-report.pdf
application/pdf, Content-Disposition includes
the original filename, and the SHA-256 is returned in the
X-Checksum-Sha256 header so CI jobs can verify integrity without
parsing JSON.
Revoke & reissue
Invalidates the current password and issues a fresh one. Use this when the recipient has lost their password or when a password may have been exposed. The document itself and the download URL remain unchanged — only the password rotates.
curl -X POST \ -H "Authorization: Bearer $SECDL_API_KEY" \ https://api.secdl.portal42.cc/v2/documents/SDL-2026-0423-AC4F/reissue-password # Response { "password": "7BNC-MX2E-4HPV-LKS9-DRQA", // new one-time password "previous_invalidated_at": "2026-04-23T14:21:07Z" }
Errors
| Code | Meaning | When |
|---|---|---|
| 400 | invalid_request | Missing or malformed field — see error.field. |
| 401 | invalid_password | Access password does not match. After 5 failures the link self-destructs. |
| 403 | insufficient_scope | API key lacks permission on this document's organization. |
| 404 | not_found | Document ID unknown, or link was revoked. |
| 410 | gone | Expired or already consumed (max_downloads reached). |
| 429 | rate_limited | See Retry-After header. |
Rate limits
Upload: 60 requests per minute per API key. Download (per document): 10 password attempts total before self-destruct, 2 retrievals per second after authentication.