Encryption & retention

This page lists exactly what poolse encrypts, what it does not, and how long it keeps things. Read it before you put sensitive data in messages.

How encryption works

Each workspace has its own 32-byte data encryption key (DEK), created with the workspace. The DEK is stored wrapped (encrypted) by a server master key with AES-256-GCM. Fields that are encrypted use AES-256-GCM under the workspace's DEK, with a fresh random 12-byte IV for every value.

poolse's servers hold the master key and decrypt these fields whenever they serve your API, realtime, webhook or push traffic. This is encryption at rest, not end-to-end encryption.

Encrypted with the workspace key

DataNotes
Message bodyEncrypted on send and on every edit. An edit overwrites the old body; no history is kept.
Webhook signing secretsDecrypted on each delivery to sign it.
Push credentialsYour APNs .p8 key and Firebase service account JSON. Write-only: the dashboard never shows them again.
Storage credentialsThe secret key of your own bucket, if you connect one. Write-only, like push credentials.

Stored without application-level encryption

These are stored as plain values in the database or object storage.

DataNotes
Conversation preview (last_message_preview)The first 80 characters of the conversation's latest message, or "Attachment" / "N attachments" when it has no text. System messages (call records) are stored whole. Editing or deleting the latest message rewrites it. Returned on conversations in the API.
Webhook delivery payloadsThe full event body, including message text and the preview field, kept for 30 days. See Retention.
Attachment filesStored as uploaded in poolse's object storage (Cloudflare R2), or, with your own bucket connected, in your bucket under your provider's settings. poolse does not encrypt the bytes itself.
Attachment metadataContent type, size, original filename, SHA-256.
Conversationsname, avatar_url, custom_data, settings.
End-usersexternal_id, display_name, avatar_url, custom_data.
Call recordsWho called whom, when, how long, the outcome.
Audit logActions, actor and target ids, and metadata. Message text is not included.

API key secrets are not stored at all: poolse keeps the key's prefix and an HMAC-SHA256 hash of the secret, made with a server-side pepper. That is why the dashboard shows a key only once.

In transit

The hosted API is served over HTTPS (https://api.poolse.dev), and the SDK derives the WebSocket URL from it, so production traffic uses wss://.

Who at poolse can see what

The poolse operator console shows metadata only: message counts, member lists and timestamps. It has no code path that decrypts a message body. That is a property of the console's code, not a cryptographic barrier: the same servers that decrypt bodies to serve your app hold the key.

Retention

DataKept
Webhook deliveries (payload, your response status and body, error)30 days, removed by a daily sweep. Deleting a webhook deletes its deliveries straight away.
Pending attachments (upload URL requested, never sent in a message)Removed after 24 hours.
Deleted messagesThe row stays as a tombstone; the body is cleared and its attachments are removed from storage at once.
Everything elseUntil you delete it, or delete the workspace. poolse does not expire messages, conversations, users, call records or audit log entries.

There is no API to delete an end-user or a conversation. To stop a user, block them (is_blocked: true on POST /v1/users).

Deleting a workspace

Deleting your workspace from the Danger zone removes, in this order: the attachment files from storage (best effort, one by one; with your own bucket connected, the files poolse wrote there under the key prefix), the workspace's audit log, then the workspace row. That cascades to every row that belongs to it, including the wrapped DEK, messages, users, conversations, API keys, webhooks and their deliveries, push credentials, devices and call records.

With the DEK gone, nothing in the live database can decrypt that workspace's message bodies any more. This page makes no statement about copies held in database backups.

What this means for you

  • Keep your own copy of anything you must retain. Subscribe to message.created and store what you need when it happens.
  • Treat previews as message content. last_message_preview, webhook payloads and push notifications all carry message text.
  • Don't put secrets in custom_data or names. They are not encrypted.
Encryption & retention · poolse docs