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