The Business API can create messages, check delivery status, withdraw open sendings, and manage contacts. Webhooks reliably notify your software of important state changes.
Quick start
Create a key in your account under Interface & Webhooks. It will be displayed in full only once. SchlüsselBot stores only its checksum.
curl -X POST https://schluesselbot.de/v1/messages \
-H "Authorization: Bearer sb_live_XXXXXXXX_…" \
-H "Idempotency-Key: auftrag-4711-versand-1" \
-H "Content-Type: application/json" \
-d '{
"recipient_email": "empfaenger@firma.de",
"message": "Zugang: kunde42 / T7!kq2wZ",
"ttl_hours": 48,
"sender_name": "Kanzlei Weber"
}'
For server-to-server calls, the Bearer token is sufficient. External browser applications additionally require an origin approved by the key, for example https://portal.firma.de.
Permissions
| Scope | Allowed |
|---|---|
messages:send | Send messages and already client-side encrypted attachments |
messages:read | Check the status of your own API dispatch |
messages:withdraw | Permanently block and remove an outstanding, own message |
contacts:read | Read and search your own contacts |
contacts:write | Create, edit, and delete your own contacts |
Messages
/v1/messagesScope messages:send/v1/send remains available as a compatible alias. When using automated retries, always use a unique Idempotency-Key. The same key with identical content returns the same response within 24 hours; different content results in HTTP 409.
message, the plaintext briefly reaches the SchlüsselBot memory and is encrypted there. For provider blindness, your application encrypts itself, sends ciphertext, and distributes the key separately using delivery_mode: "separate". A transmitted fragment enables automatic email delivery, but still reaches the server.| Field | Mandatory | Content |
|---|---|---|
recipient_email | yes | Recipient address |
message | alternative | Plain text up to 256 KB, encrypted on server side in storage |
ciphertext | alternative | Base64: 12-byte nonce + AES-256-GCM ciphertext + 16-byte tag |
fragment | for automatic delivery | 32-byte key as 43-character base64url without padding |
attachment_ciphertext | no | Already encrypted file container, up to 100 MB of raw data |
delivery_mode | no | automatic or separate |
ttl_hours | no | 1 to 168, default 48 |
sender_name | no | Display name up to 80 characters |
personal_note | no | Email note up to 500 characters; external links are removed |
notify_email | no | E-Mail for delivery confirmation after retrieval |
{
"id": "8f3c…",
"link": "https://schluesselbot.de/r/8f3c…#kQ2…",
"expires_at": "2026-08-20T09:00:00+00:00",
"modus": "encrypted by the server",
"delivery": "automatic",
"mail_sent": true,
"has_attachment": false
}
/v1/messages/{id}Scope messages:readReturns ready , consumed , withdrawn , expired , or unknown if the safe cannot currently be verified. A failure is never falsely reported as a retrieval.
/v1/messages/{id}Scope messages:withdrawFirst permanently locks the transaction and then removes it from the vault. If physical deletion temporarily fails, the message remains inaccessible and cleanup will automatically be retried.
Contacts
Contact names and email addresses are encrypted in the database using AES-256-GCM. API responses are not cached.
| Method | Path | Scope | Function |
|---|---|---|---|
| GET | /v1/contacts?q=&cursor=&limit= | contacts:read | Search and list page by page, up to 100 per page |
| POST | /v1/contacts | contacts:write | {"name":"Anna","email":"anna@example.de"} |
| GET | /v1/contacts/{id} | contacts:read | Load single contact |
| PATCH | /v1/contacts/{id} | contacts:write | Name or address partially changed |
| DELETE | /v1/contacts/{id} | contacts:write | Delete contact |
Webhooks
Selectable events: created, consumed, withdrawn, expired, and delivery_failed. SchlüsselBot permanently stores each event prior to the first delivery attempt. An HTTP 2xx response is expected within five seconds. In case of failure, up to eight retries will occur over approximately three days: after 1 minute, 5 minutes, 15 minutes, 1 hour, and then at increasing intervals.
{
"api_version": "v1",
"event_id": "consumed:8f3c…",
"event": "consumed",
"zeit": "2026-08-19T10:12:33+00:00",
"daten": {"id": "8f3c…", "mit_anhang": false}
}
X-SchluesselBot-Event: consumed
X-SchluesselBot-Event-Id: consumed:8f3c…
X-SchluesselBot-Delivery: wd_…
X-SchluesselBot-Signature: sha256=<HMAC-SHA256 des unveränderten Roh-Rumpfs>
Verify the signature over the unmodified request body and process event_id idempotently. The secret is shown once upon creation and stored separately by SchlüsselBot, AES-256-GCM encrypted.
// PHP
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($expected, $_SERVER['HTTP_X_SCHLUESSELBOT_SIGNATURE'] ?? '')) {
http_response_code(401); exit;
}
Webhook targets must be publicly reachable, HTTPS, and without redirection. Private, loopback, link-local, documentation, and Tailscale/CGNAT networks are blocked; the resolved public IP is permanently fixed for the actual connection setup.
Errors and limitations
- 400 invalid input · 401 invalid key · 403 scope, plan or origin not permitted · 409 duplicate/idempotency conflict · 413 too large · 429 sending limit · 502/503 temporary operational error.
- Up to 10 active API keys, 10 browser origins per key, and 5 webhooks per business account.
- The API does not list or read message contents. They are only readable by the recipient and only once.
- When encountering 429, 502, or 503
Retry-After, observe and reuse the sameIdempotency-Key.