End-to-end encryption
Decide who can read your messages
Pick a data level for each number. Polymorfa stores chats regionally, stores nothing, or relays only ciphertext while the Signal keys stay on your servers or in the browser.
- Data levels for each number
- 4
- Signal keys we hold on zero-knowledge numbers
- 0
- Longest queue copy without hosted storage
- 24 h
- Databases with official adapters
- 9
Data levels
One number, four ways to hold its data
On hosted and pass-through numbers, Polymorfa handles message content to deliver it. On the two zero-knowledge levels, only you can decrypt it.
Zero-knowledge numbers
We carry the traffic, you hold the keys
Polymorfa keeps the WhatsApp connection, receipts, retries and pacing. Every operation that needs a private key goes to your Signal service, and message plaintext never reaches our servers, in memory or on disk.
Your database
Sync into the database you already run
Every SDK language shares one storage contract. Point a webhook handler or an event stream at an adapter, and chats, messages, contacts, media and history land in your own tables.
Direct media and history
Media and history without a storage bill
The SDK uploads media to WhatsApp and downloads history chunks itself, so neither adds storage cost. Both are free, with no meter.
- On zero-knowledge numbers, the media key and plaintext never reach Polymorfa
- Your Signal service handles history for zero-knowledge numbers entirely
- Plaintext retry and event buffers are off for every number, enforced by the database
In your code
Keep your copy in a few lines
Store events in your database, upload media straight to WhatsApp, archive it to your bucket or read hosted chats.
import { webhooks } from "@polymorfa/sdk";
import { storageWebhookHandler } from "@polymorfa/storage";
import { postgresAdapter } from "@polymorfa/storage/postgres";
const store = storageWebhookHandler(
postgresAdapter({ connectionString: process.env.DATABASE_URL! }),
);
export async function POST(request: Request) {
const event = await webhooks.verify({
body: await request.arrayBuffer(),
signature: request.headers.get("x-webhook-signature") ?? "",
secret: process.env.WEBHOOK_SECRET!,
});
await store(event); // chats, messages, contacts, media
return Response.json({ received: event.id });
}API surface
The data API
Hosted chats through the Messaging API, and the commands the SDK uses for direct media and history.
Hosted Message Storage
- GET
/List stored chatsmessaging/ {number}/ chats - GET
/Get one chatmessaging/ {number}/ chats/ {chat} - GET
/Page through stored messagesmessaging/ {number}/ chats/ {chat}/ messages - GET
/Download stored mediamessaging/ {number}/ chats/ {chat}/ messages/ {id}/ media
Direct media and history
- POST
/Upload ciphertext through the numbermessaging/ {number}/ media/ relay-uploads - POST
/Short-lived grant to upload from your networkmessaging/ {number}/ media/ upload-grants - POST
/Ask the phone to send a chunk againmessaging/ {number}/ history/ chunk-retries - Event
history.syncA history chunk is ready to download
Start with a free number
Your first real number is free. Add more numbers on Pay-As-You-Go.