feat: initial openapi content (v0.1.0)

- Vollständige OpenAPI 3.1 Spezifikation
- Alle Endpoints, Schemas, Webhooks, Error-Responses
- Beispiel-Requests für minimal/async/webhook-Modi
- Initiale CHANGELOG.md
This commit is contained in:
Stefan Schmidt-Egermann 2026-04-25 12:26:00 +02:00
parent 2db26ced18
commit b13fba1d75
Signed by: SSE
GPG key ID: DE0FCB225FF13A91
5 changed files with 720 additions and 49 deletions

18
CHANGELOG.md Normal file
View file

@ -0,0 +1,18 @@
# Changelog
Alle nennenswerten Änderungen an der OpenAPI-Spec werden hier dokumentiert.
Format orientiert sich an [Keep a Changelog](https://keepachangelog.com/de/1.1.0/).
## [Unreleased]
## [1.0.0] — 2026-04-25
### Hinzugefügt
- Initiale Veröffentlichung der OpenAPI 3.1 Spezifikation
- Endpoints: `POST /captures`, `GET /captures`, `GET /captures/{id}`,
`GET /captures/{id}/pdf`, `POST /verify`, `GET /usage`
- Drei Aufruf-Modi: `sync`, `async`, `webhook`
- Webhook-Events: `capture.ready`, `capture.failed`
- Komplette Schema-Definitionen für `Capture`, `VerifyResult`, `Usage`, `Error`
- Beispiel-Requests für minimal, async-mit-Referenz, und webhook-mit-Co-Branding

20
LICENSE
View file

@ -1,9 +1,21 @@
MIT License
Copyright (c) 2026 hightrusted
Copyright (c) 2026 hightrusted GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,6 +1,6 @@
# hightrusted CAPTURE — OpenAPI Specification
> **Status:** v0.1 Preview — API stabil, Spec ist Single Source of Truth für alle SDKs
> **Status:** v1.0.0 — Single Source of Truth für alle SDKs
OpenAPI 3.1 Spezifikation der [hightrusted CAPTURE API](https://capture.hightrusted.net) —
forensische Web-Captures mit qualifiziertem Zeitstempel nach RFC 3161 / eIDAS Art. 41.
@ -8,16 +8,14 @@ forensische Web-Captures mit qualifiziertem Zeitstempel nach RFC 3161 / eIDAS Ar
Diese Spec ist die **maschinenlesbare Referenz**, aus der wir alle anderen Artefakte
ableiten: SDKs, Client-Bibliotheken, Postman-Collection, gerenderte Doku.
**Made in Germany.** Server in Deutschland. DSGVO-nativ. Kein US-Cloud-Anbieter
in der Verarbeitungskette.
**Made in Germany.** Server in Deutschland. DSGVO-nativ.
## Inhalt
- `openapi.yaml` — die OpenAPI 3.1 Spec
- `examples/` — Beispiel-Requests und -Responses für jeden Endpoint
- `CHANGELOG.md` — Versions-History der Spec
- `CHANGELOG.md` — Versions-History
## Validierung
## Validieren
```bash
# Mit redocly cli
@ -32,25 +30,47 @@ npx @stoplight/spectral-cli lint openapi.yaml
```bash
# HTML-Doku mit Redoc bauen
npx @redocly/cli build-docs openapi.yaml -o ./docs/index.html
```
Ergebnis: statische HTML-Datei mit kompletter API-Doku, die du auf jedem Webserver
ablegen kannst.
# Mit Swagger UI lokal anzeigen
npx swagger-ui-watcher openapi.yaml
```
## Client-Generierung
Aus dieser Spec lassen sich automatisch Clients für 50+ Sprachen erzeugen
(siehe https://openapi-generator.tech). Wir pflegen offizielle Clients für
Python, Node.js und PHP — siehe [Verwandte Repositorys](#verwandte-repositorys).
Aus dieser Spec lassen sich automatisch Clients für 50+ Sprachen erzeugen:
```bash
# Beispiel: Go-Client erzeugen
openapi-generator-cli generate \
npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml \
-g go \
-o ./clients/go
```
Wir pflegen offizielle Clients **handgeschrieben** für Python, Node.js und PHP
— sie sind idiomatischer als generierte Clients und haben getestete
Webhook-Verifikation, typisierte Exceptions und ergonomische APIs.
## Endpoints (Übersicht)
| Endpoint | Beschreibung | Auth |
|--------------------------------|--------------------------------------------|--------|
| `POST /captures` | Capture erstellen (sync/async/webhook) | Bearer |
| `GET /captures` | Captures auflisten (paginiert) | Bearer |
| `GET /captures/{id}` | Status & Metadaten abrufen | Bearer |
| `GET /captures/{id}/pdf` | PDF herunterladen | Bearer |
| `POST /verify` | Verifizieren (per ID, URL, oder PDF) | — |
| `GET /usage` | eigenen Verbrauch abrufen | Bearer |
## Webhook-Events
| Event | Wann |
|-------------------|-----------------------------------------|
| `capture.ready` | PDF und Zeitstempel fertig |
| `capture.failed` | Fehler beim Rendern oder TSA |
Webhook-Retry: 3 Versuche mit exponentiellem Backoff (1 min, 5 min, 30 min).
Signaturverifikation per HMAC-SHA-256 (`X-Hightrusted-Signature: sha256=...`).
## Versionierung
API-Versionen sind im Pfad: `/api/v1/...`
@ -61,25 +81,6 @@ API-Versionen sind im Pfad: `/api/v1/...`
Diese OpenAPI-Spec verfolgt die API 1:1. Änderungen sind in `CHANGELOG.md`
nachvollziehbar.
## Endpoints (Übersicht)
| Endpoint | Beschreibung | Auth |
|--------------------------------|---------------------------------------|--------|
| `POST /captures` | Capture erstellen (sync/async/webhook)| Bearer |
| `GET /captures/{id}` | Status & Metadaten abrufen | Bearer |
| `GET /captures/{id}/pdf` | PDF herunterladen | Bearer |
| `GET /captures/{id}/verify` | Capture verifizieren | — |
| `GET /usage` | eigenen Verbrauch abrufen | Bearer |
## Webhook-Events
| Event | Wann |
|-------------------|-----------------------------------------|
| `capture.ready` | PDF und Zeitstempel fertig |
| `capture.failed` | Fehler beim Rendern oder TSA |
Webhook-Retry: 3 Versuche mit exponentiellem Backoff (1 min, 5 min, 30 min).
## Verwandte Repositorys
**Im selben Produkt** ([`hightrusted-capture`](https://git.hightrusted.net/hightrusted-capture)):
@ -92,20 +93,20 @@ Webhook-Retry: 3 Versuche mit exponentiellem Backoff (1 min, 5 min, 30 min).
**Plattform-übergreifend** ([`hightrusted`](https://git.hightrusted.net/hightrusted)):
- [`platform`](https://git.hightrusted.net/hightrusted/platform) — Plattform-Übersicht, Architektur, Produkt-Liste
- [`developer-portal`](https://git.hightrusted.net/hightrusted/developer-portal) — gemeinsame Konventionen, Auth, Errors, Rate-Limits
- [`compliance`](https://git.hightrusted.net/hightrusted/compliance) — DSGVO, AGB-Templates, Whitepaper
- [`platform`](https://git.hightrusted.net/hightrusted/platform)
- [`developer-portal`](https://git.hightrusted.net/hightrusted/developer-portal)
- [`compliance`](https://git.hightrusted.net/hightrusted/compliance)
## Support
- **Doku:** https://capture.hightrusted.net/api/docs
- **Status-Page:** https://status.hightrusted.net
- **Status:** https://status.hightrusted.net
- **Developer Support:** developers@hightrusted.net
- **Sicherheitslücken:** siehe [SECURITY.md](./SECURITY.md)
## Lizenz
MIT — siehe [LICENSE](./LICENSE).
MIT — siehe [LICENSE](./LICENSE). Spec und Beispiele dürfen frei für eigene
Implementierungen genutzt werden.
---

View file

@ -25,9 +25,3 @@ Während der `v0.x`-Phase werden nur die jeweils aktuellste Minor-Version und
deren letzte zwei Patch-Versionen aktiv mit Sicherheits-Updates versorgt.
Ab `v1.0` gilt: aktuelle Major + vorherige Major (12 Monate Übergangsfrist).
## Out of Scope
Diese Policy gilt für die SDKs in diesem Repository und die zugehörige
hightrusted CAPTURE API. Für Schwachstellen in anderen hightrusted-Produkten
(SIGN, ID, MEET, PAY, …) gilt jeweils die dortige `SECURITY.md`.

646
openapi.yaml Normal file
View file

@ -0,0 +1,646 @@
openapi: 3.1.0
info:
title: hightrusted CAPTURE API
version: "1.0.0"
summary: Forensische Web-Captures mit qualifiziertem EU-Zeitstempel
description: |
Forensische Web-Captures mit qualifiziertem EU-Zeitstempel nach
RFC 3161 / eIDAS Art. 41.
Die API nimmt eine URL entgegen, rendert die Seite vollständig (inkl.
JavaScript), liefert sie als PDF/A-3 zurück und versieht das Ergebnis mit
einem qualifizierten Zeitstempel. Das Ergebnis ist gerichtsverwertbar und
Jahre später noch verifizierbar — auch nachdem die Original-Seite längst
offline ist.
**Drei Aufruf-Modi:**
- `sync` (Default) — Server wartet bis zu 30 s und liefert das fertige Capture
- `async` — Server liefert sofort `id`, Client pollt `GET /captures/{id}`
- `webhook` — Server liefert das fertige Capture per HTTP-POST an `webhook_url`
**Authentifizierung:** Bearer-Token mit API-Key. Erzeugung im Dashboard:
https://capture.hightrusted.net/dashboard/api-keys
**Made in Germany.** Server in Deutschland. DSGVO-nativ. eIDAS-konform.
Kein US-Cloud-Anbieter in der Verarbeitungskette.
contact:
name: hightrusted Developer Support
email: developers@hightrusted.net
url: https://capture.hightrusted.net/api
license:
name: hightrusted API Terms
url: https://hightrusted.net/legal/api-terms
termsOfService: https://hightrusted.net/legal/terms
servers:
- url: https://capture.hightrusted.net/api/v1
description: Production
security:
- bearerAuth: []
tags:
- name: Captures
description: Capture-Erstellung, -Status, -Download
- name: Verify
description: Verifikation gespeicherter Captures (kostenlos, ohne Quota)
- name: Account
description: Verbrauch und Quota-Status
paths:
# ════════════════════════════════════════════════════════════════════════
/captures:
post:
tags: [Captures]
summary: Capture erstellen
operationId: createCapture
description: |
Erstellt eine forensische Web-Capture in einem von drei Modi:
- **sync** (Default): wartet bis zu 30 s und liefert das fertige PDF
- **async**: liefert sofort `id`, Client pollt `GET /captures/{id}`
- **webhook**: Server liefert das fertige Capture per HTTP-POST
**Shortlink-Auflösung:** `maps.app.goo.gl`-Links und ähnliche Shortlinks
werden automatisch aufgelöst. Das Originalfeld `url` bleibt für die
Nachweisbarkeit erhalten.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CaptureRequest"
examples:
minimal:
summary: Minimal — synchron
value:
url: https://example.com/article/123
async_with_reference:
summary: Asynchron mit Referenz
value:
url: https://example.com/page
mode: async
reference: case-2026-001
webhook_full:
summary: Webhook mit Co-Branding
value:
url: https://example.com/page
mode: webhook
webhook_url: https://api.deinedomain.de/capture-callback
reference: case-2026-001
viewport:
width: 1920
height: 1080
co_branding:
logo_url: https://kanzlei-mueller.de/logo.png
footer_text: Kanzlei Müller — Beweissicherung
responses:
"200":
description: Synchron — fertiges Capture
content:
application/json:
schema:
$ref: "#/components/schemas/Capture"
"202":
description: Asynchron / Webhook — Capture in Bearbeitung
content:
application/json:
schema:
$ref: "#/components/schemas/CaptureQueued"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"402":
$ref: "#/components/responses/QuotaExceeded"
"422":
$ref: "#/components/responses/UnreachableUrl"
"429":
$ref: "#/components/responses/RateLimited"
"503":
$ref: "#/components/responses/TsaUnavailable"
get:
tags: [Captures]
summary: Eigene Captures auflisten
operationId: listCaptures
description: |
Listet die Captures des Accounts, paginiert per Cursor.
parameters:
- in: query
name: status
schema:
type: string
enum: [queued, rendering, ready, failed]
description: Nur Captures mit diesem Status
- in: query
name: reference
schema:
type: string
description: Filter auf Custom-Reference (z.B. Mandanten-ID)
- in: query
name: limit
schema:
type: integer
minimum: 1
maximum: 100
default: 25
- in: query
name: cursor
schema:
type: string
description: Cursor aus vorheriger Response
responses:
"200":
description: Paginierte Liste
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: "#/components/schemas/Capture"
next_cursor:
type: string
nullable: true
"401":
$ref: "#/components/responses/Unauthorized"
# ════════════════════════════════════════════════════════════════════════
/captures/{id}:
get:
tags: [Captures]
summary: Capture-Status / Detail
operationId: getCapture
description: |
Liefert das Capture-Objekt mit aktuellem `status`. Solange `queued` oder
`rendering`, fehlen `pdf_url` und `timestamp`.
parameters:
- $ref: "#/components/parameters/CaptureId"
responses:
"200":
description: Capture-Detail
content:
application/json:
schema:
$ref: "#/components/schemas/Capture"
"401":
$ref: "#/components/responses/Unauthorized"
"404":
$ref: "#/components/responses/CaptureNotFound"
# ════════════════════════════════════════════════════════════════════════
/captures/{id}/pdf:
get:
tags: [Captures]
summary: PDF herunterladen
operationId: downloadCapturePdf
description: |
Liefert PDF/A-3 mit eingebettetem RFC-3161-Zeitstempel.
Verfügbar erst, wenn `status: ready`.
parameters:
- $ref: "#/components/parameters/CaptureId"
responses:
"200":
description: PDF-Datei
headers:
Content-Disposition:
schema:
type: string
example: attachment; filename="capture_550e....pdf"
content:
application/pdf:
schema:
type: string
format: binary
"401":
$ref: "#/components/responses/Unauthorized"
"404":
$ref: "#/components/responses/CaptureNotFound"
"409":
description: Capture noch nicht fertig
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
# ════════════════════════════════════════════════════════════════════════
/verify:
post:
tags: [Verify]
summary: Capture / PDF verifizieren
operationId: verifyCapture
description: |
Prüft, ob ein Capture / PDF noch gültig ist (Hash, Zeitstempel,
Audit-Chain).
**Kostenlos für alle Tarife. Keine Quota-Belastung.**
Drei Eingabe-Modi:
- Per Capture-ID (`source: "550e..."`)
- Per Verify-URL (`source: "https://verify.hightrusted.net/c/..."`)
- Per PDF-Upload (multipart/form-data, Feld `pdf`)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [source]
properties:
source:
type: string
description: Capture-ID oder Verify-URL
examples:
by_id:
summary: Per Capture-ID
value:
source: 550e8400-e29b-41d4-a716-446655440000
by_url:
summary: Per Verify-URL
value:
source: https://verify.hightrusted.net/c/550e...
multipart/form-data:
schema:
type: object
properties:
pdf:
type: string
format: binary
description: PDF-Datei zur lokalen Verifikation
responses:
"200":
description: Verifikations-Ergebnis
content:
application/json:
schema:
$ref: "#/components/schemas/VerifyResult"
"404":
description: Quelle nicht auffindbar
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
# ════════════════════════════════════════════════════════════════════════
/usage:
get:
tags: [Account]
summary: Eigener Verbrauch
operationId: getUsage
description: Aktueller Verbrauch und Restkontingent für die laufende Periode.
responses:
"200":
description: Verbrauchs-Status
content:
application/json:
schema:
$ref: "#/components/schemas/Usage"
"401":
$ref: "#/components/responses/Unauthorized"
# ════════════════════════════════════════════════════════════════════════════
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: ht_live_xxx | ht_test_xxx
parameters:
CaptureId:
in: path
name: id
required: true
schema:
type: string
format: uuid
example: 550e8400-e29b-41d4-a716-446655440000
description: ID des Captures
schemas:
# ─────────────────────────────────────────────────────────
CaptureRequest:
type: object
required: [url]
properties:
url:
type: string
format: uri
description: Zu erfassende URL. Pflicht.
example: https://example.com/article/123
mode:
type: string
enum: [sync, async, webhook]
default: sync
webhook_url:
type: string
format: uri
description: Pflicht bei `mode=webhook`
reference:
type: string
maxLength: 128
description: Eigene Referenz (Mandanten-ID, Akten-Nr.)
viewport:
type: object
properties:
width:
type: integer
minimum: 360
maximum: 2560
default: 1280
height:
type: integer
minimum: 480
maximum: 4096
default: 800
wait_until:
type: string
enum: [load, domcontentloaded, networkidle]
default: networkidle
full_page:
type: boolean
default: true
description: Komplette Seite scrollen
co_branding:
type: object
description: Eigenes Logo & Footer im PDF (ab Tarif Growth)
properties:
logo_url:
type: string
format: uri
footer_text:
type: string
maxLength: 200
# ─────────────────────────────────────────────────────────
Capture:
type: object
properties:
id:
type: string
format: uuid
example: 550e8400-e29b-41d4-a716-446655440000
status:
type: string
enum: [queued, rendering, ready, failed]
url:
type: string
format: uri
description: Aufgelöste End-URL (nach Shortlink-Auflösung)
url_input:
type: string
format: uri
description: Vom Caller übergebene URL (für die Beweisführung)
reference:
type: string
nullable: true
pdf_url:
type: string
format: uri
nullable: true
description: Authentifizierter Download-Endpoint
verify_url:
type: string
format: uri
description: Öffentliche Verifikations-URL
timestamp:
$ref: "#/components/schemas/Timestamp"
document_hash:
type: string
example: sha256:a3f2...
created_at:
type: string
format: date-time
completed_at:
type: string
format: date-time
nullable: true
required: [id, status, created_at]
# ─────────────────────────────────────────────────────────
CaptureQueued:
type: object
properties:
id:
type: string
format: uuid
status:
type: string
enum: [queued]
poll_url:
type: string
format: uri
description: Endpoint für Status-Polling (nur bei `mode=async`)
created_at:
type: string
format: date-time
required: [id, status, created_at]
# ─────────────────────────────────────────────────────────
Timestamp:
type: object
properties:
tsa:
type: string
example: open-tsa.eu
issued_at:
type: string
format: date-time
algorithm:
type: string
example: SHA-256
serial:
type: string
example: "0x4F8B..."
# ─────────────────────────────────────────────────────────
VerifyResult:
type: object
properties:
valid:
type: boolean
capture_id:
type: string
format: uuid
document_hash:
type: string
timestamp:
type: object
properties:
tsa:
type: string
issued_at:
type: string
format: date-time
valid:
type: boolean
chain_valid:
type: boolean
audit_log:
type: object
properties:
present:
type: boolean
chain_valid:
type: boolean
anchored_in_open_tsa:
type: boolean
details:
type: array
items:
type: object
properties:
check:
type: string
example: tsr_signature
status:
type: string
enum: [pass, fail, warn]
message:
type: string
required: [valid]
# ─────────────────────────────────────────────────────────
Usage:
type: object
properties:
plan:
type: string
example: growth
period_start:
type: string
format: date
period_end:
type: string
format: date
included_calls:
type: integer
used_calls:
type: integer
overage_calls:
type: integer
overage_cost_eur:
type: number
format: float
rate_limit:
type: object
properties:
per_minute:
type: integer
current_minute:
type: integer
# ─────────────────────────────────────────────────────────
Error:
type: object
properties:
error:
type: object
properties:
code:
type: string
example: quota_exceeded
message:
type: string
request_id:
type: string
example: req_4xK7p2nQ8mR9
required: [code, message, request_id]
required: [error]
responses:
BadRequest:
description: Validierungsfehler
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Unauthorized:
description: API-Key fehlt, abgelaufen oder widerrufen
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
QuotaExceeded:
description: Monats-Quota voll, kein PAYG aktiv
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
CaptureNotFound:
description: Capture-ID existiert nicht oder gehört zu fremdem Key
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
UnreachableUrl:
description: Quellseite konnte nicht geladen werden
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
RateLimited:
description: |
Zu viele Anfragen pro Minute. Header `Retry-After` beachten.
headers:
Retry-After:
schema:
type: integer
description: Sekunden bis zum nächsten erlaubten Request
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
TsaUnavailable:
description: |
open-tsa.eu temporär nicht erreichbar. Automatischer Retry empfohlen.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
webhooks:
capture.ready:
post:
tags: [Webhook-Events]
summary: Capture fertig (PDF und Zeitstempel verfügbar)
requestBody:
content:
application/json:
schema:
type: object
properties:
event:
type: string
enum: [capture.ready]
capture:
$ref: "#/components/schemas/Capture"
required: [event, capture]
responses:
"200":
description: Webhook empfangen
capture.failed:
post:
tags: [Webhook-Events]
summary: Capture fehlgeschlagen
requestBody:
content:
application/json:
schema:
type: object
properties:
event:
type: string
enum: [capture.failed]
capture:
$ref: "#/components/schemas/Capture"
error:
$ref: "#/components/schemas/Error"
required: [event, capture]
responses:
"200":
description: Webhook empfangen