Darstellung
Schnellstart
Diese Seite führt in vier Schritten vom ersten Pairing bis zu einer angelegten Nachricht. Als Grundlage dient eine kleine Hilfsfunktion, die den HMAC-Umschlag baut und den Request sendet. Alle weiteren Beispiele in der Dokumentation lassen sich damit nachbauen.
Bevor es losgeht
Du brauchst Partner-Zugangsdaten und einen freigegebenen Origin, siehe Erste Schritte. Zum Ausprobieren empfehlen wir die Entwicklungsumgebung https://api.dev.ebonaut.io.
Hilfsfunktion call()
Die Funktion verschlüsselt den Body, signiert ihn und sendet ihn als form-data. Sie liefert die geparste JSON-Antwort.
js
const crypto = require('node:crypto')
const BASE_URL = 'https://api.dev.ebonaut.io'
const ORIGIN = 'https://partner.example.com' // freigegebener Origin
async function call(path, creds, body = {}) {
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(creds.encryptionKey, 'utf8'), iv)
const encrypted = Buffer.concat([
cipher.update(JSON.stringify({
timestamp: Math.floor(Date.now() / 1000),
hmac_unique: crypto.randomUUID(),
body,
}), 'utf8'),
cipher.final(),
])
const message = `${iv.toString('hex')}:${encrypted.toString('hex')}`
const form = new FormData()
form.append('hmac_ID', creds.hmacId)
form.append('message', message)
form.append('hmac', crypto.createHmac('sha256', creds.secretKey).update(message).digest('hex'))
const res = await fetch(`${BASE_URL}/${path}`, {
method: 'POST',
headers: { Origin: ORIGIN },
body: form,
})
const data = await res.json()
return { status: res.status, data }
}php
const BASE_URL = 'https://api.dev.ebonaut.io';
const ORIGIN = 'https://partner.example.com'; // freigegebener Origin
function call(string $path, array $creds, array $body = []): array
{
$iv = random_bytes(16);
$encrypted = openssl_encrypt(
json_encode([
'timestamp' => time(),
'hmac_unique' => bin2hex(random_bytes(16)),
'body' => (object) $body,
]),
'aes-256-cbc',
$creds['encryptionKey'],
OPENSSL_RAW_DATA,
$iv
);
$message = bin2hex($iv) . ':' . bin2hex($encrypted);
$ch = curl_init(BASE_URL . '/' . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Origin: ' . ORIGIN],
CURLOPT_POSTFIELDS => [
'hmac_ID' => $creds['hmacId'],
'message' => $message,
'hmac' => hash_hmac('sha256', $message, $creds['secretKey']),
],
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['status' => $status, 'data' => json_decode($raw, true)];
}python
import hashlib, hmac, json, os, time, uuid
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
BASE_URL = 'https://api.dev.ebonaut.io'
ORIGIN = 'https://partner.example.com' # freigegebener Origin
def call(path, creds, body=None):
iv = os.urandom(16)
plaintext = json.dumps({
'timestamp': int(time.time()),
'hmac_unique': str(uuid.uuid4()),
'body': body or {},
})
cipher = AES.new(creds['encryption_key'].encode(), AES.MODE_CBC, iv)
message = iv.hex() + ':' + cipher.encrypt(pad(plaintext.encode(), AES.block_size)).hex()
res = requests.post(
f'{BASE_URL}/{path}',
headers={'Origin': ORIGIN},
data={
'hmac_ID': creds['hmac_id'],
'message': message,
'hmac': hmac.new(creds['secret_key'].encode(), message.encode(), hashlib.sha256).hexdigest(),
},
)
return res.status_code, res.json()Die Beispiele unten verwenden die Node.js-Variante. Mit den anderen Sprachen funktionieren sie identisch.
Schritt 1: Pairing-Code erzeugen
Dieser Request wird mit den Partner-Zugangsdaten signiert.
js
const partner = { hmacId: '<partner_hmac_id>', secretKey: '<partner_secret>', encryptionKey: '<partner_enc_key>' }
const { data } = await call('codesetup/set', partner)
console.log(data)
// { codesetup_ID: "483920", codesetup_url: "https://app.ebonaut.io/activate/483920" }Gib codesetup_url (oder den sechsstelligen Code) an deine Kund:in weiter. Diese bestätigt die Verbindung in der eBOnaut-App, mehr dazu im Pairing-Ablauf.
Schritt 2: Kunden-Zugangsdaten abholen
Frag alle 5 bis 10 Sekunden ab, bis die Kund:in aktiviert hat (409 bedeutet: noch nicht aktiviert).
js
let customer
while (!customer) {
const { status, data } = await call(`codesetup/get/${codesetupId}`, partner)
if (status === 200) {
customer = {
hmacId: data.hmac_ID,
secretKey: data.hmac_secret_key,
encryptionKey: data.hmac_encryption_key,
}
} else if (status === 409) {
await new Promise((r) => setTimeout(r, 5000))
} else {
throw new Error(`Pairing fehlgeschlagen: ${status} ${JSON.stringify(data)}`)
}
}
// customer jetzt sicher speichern, die Daten sind nur einmal abrufbar!Zugangsdaten sofort speichern
Die Kunden-Zugangsdaten werden nur einmal ausgeliefert. Leg sie umgehend verschlüsselt ab (Secret-Manager oder verschlüsselte Datenbank), nicht im Quellcode oder in Logs.
Schritt 3: Verbindung bestätigen
Der erste Request mit den Kunden-Zugangsdaten verlängert deren Gültigkeit von 3 Stunden auf 3 Monate. Ruf dafür user/me auf:
js
const { status, data } = await call('user/me', customer)
console.log(status, data.user_mail) // 200 "max.mustermann@example.com"Ab jetzt laufen alle Requests im Kontext dieser Kund:in.
Schritt 4: Erste Nachricht vorbereiten
Ruf zuerst die hinterlegten Zertifikate ab. Für das Senden brauchst du die cert_ID des Absender-Zertifikats:
js
const certs = await call('cert/getall', customer)
const certId = certs.data[0].cert_IDSuch anschließend die Empfänger:in:
js
const contacts = await call('contact/search', customer, { search: 'DE.Justiz.f8d8388f-1a64-' })
const recipient = contacts.data.data[0].contact_justiz_IDLeg dann einen Entwurf an:
js
const item = await call('postboxitem/set', customer, {
transmitter_cert_ID: certId,
recipient_justiz_ID: recipient,
content_subject: 'Testnachricht',
})
console.log(item.data) // "6bd525df-5b16-4a55-b5d9-ee4cf5277d7b" (postboxitem_ID)Der Entwurf hat den Status draft. Dateien hängst du über die File-API an, versendet wird mit postboxitem/send.
Wenn etwas nicht klappt
| Symptom | Häufige Ursache |
|---|---|
403 bei jedem Request | Origin fehlt oder ist nicht freigegeben, falscher Schlüssel, Systemzeit weicht ab |
403 nur bei Wiederholung | hmac_unique wurde erneut verwendet, pro Request eine neue ID erzeugen |
406 | Pflichtfeld fehlt oder Format falsch, die message nennt die betroffenen Felder |
409 bei codesetup/get | Die Kund:in hat noch nicht aktiviert, weiter abfragen |
410 bei codesetup/get | Der Code ist nach einer Stunde abgelaufen, neu erzeugen |
Weitere Hinweise findest du unter Fehler & Statuscodes.