Quickstart
Verify your API key, poll your queue, and send an encrypted reply — end to end in under five minutes.
This quickstart walks you through the four calls every PrivacyFlow client makes: verify a key, poll for an inbound message, send a reply, and confirm the service is alive. You can run every example here with curl against the public API.
Prerequisites
- A PrivacyFlow account and one app connected (Signal, SimpleX, or Session).
- An API key from the dashboard. It will look like
pf_live_…. - The appId of the app you connected. If you don’t know it, the verify call below returns it.
- A recipient contactId in the right format — a phone number for Signal, a numeric id for SimpleX, or a 66-character hex string (starting
05…) for Session. See contact formats.
Set them as shell variables so the examples copy-paste cleanly:
export PRIVACYFLOW_API_KEY="pf_live_your_key_here"
export PRIVACYFLOW_APP_ID="app_your_app_id"
export PRIVACYFLOW_BASE_URL="https://privacyflow.app"
1. Verify the service is up
GET /api/v1/health requires no auth. Use it as a smoke test from your environment.
curl -s "$PRIVACYFLOW_BASE_URL/api/v1/health"
{ "status": "ok", "version": "2.0.0" }
2. Verify your API key
GET /api/v1/auth/verify confirms your key is valid and lists every appId it can access. Use this in credential-test screens (the n8n node and MCP server both call it for that).
curl -s "$PRIVACYFLOW_BASE_URL/api/v1/auth/verify" \
-H "Authorization: Bearer $PRIVACYFLOW_API_KEY"
{ "valid": true, "appIds": ["app_your_app_id"] }
If you see an appIds array with more than one id, your key covers several apps and every poll call will round-robin across all of them.
3. Poll for incoming messages
GET /api/v1/messages/poll?limit=10 pops messages off your queue and returns them. Polling is a destructive read — once a message is popped, it’s gone.
curl -s "$PRIVACYFLOW_BASE_URL/api/v1/messages/poll?limit=10" \
-H "Authorization: Bearer $PRIVACYFLOW_API_KEY"
When your queue is empty the response is:
{ "messages": [], "count": 0, "queue": "poll:app_your_app_id" }
When a customer has messaged your app, you get back the inbound payload:
{
"messages": [
{
"appId": "app_your_app_id",
"messenger": "signal",
"contactId": "+12025550104",
"messageId": "5dd6c2c0d5d7b3a9b0c1a2c3d4e5f6a7",
"content": "Hi, I need help with my order",
"timestamp": 1718000000000,
"isGroupMessage": false,
"isCommand": false
}
],
"count": 1,
"queue": "poll:app_your_app_id"
}
Warning
Polling is destructive. Persist or process the message before the next poll; a crashed client cannot re-read it. See Concepts.
4. Send a reply
POST /api/v1/messages/send takes a messages array (up to 50 per call). Each message needs an appId, a contactId, the messenger, and the message body.
curl -s -X POST "$PRIVACYFLOW_BASE_URL/api/v1/messages/send" \
-H "Authorization: Bearer $PRIVACYFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"appId": "'"$PRIVACYFLOW_APP_ID"'",
"contactId": "+12025550104",
"messenger": "signal",
"message": "Thanks for reaching out — an agent will reply within an hour."
}
]
}'
The API responds with 202 Accepted — the message has been queued for delivery, not delivered yet.
{
"successfulMessages": [
{
"messageId": "8a5b3c0e1d2f4a6b8c0d1e2f3a4b5c6d",
"appId": "app_your_app_id",
"contactId": "+12025550104",
"messenger": "signal"
}
],
"failedMessages": [],
"totalAccepted": 1,
"totalFailed": 0
}
That’s the full loop: you received an encrypted message and replied through the same API, with no phone number, SIM, or messenger app on your side.
Next steps
- Read Concepts to understand appId isolation, destructive reads, and credential scoping.
- Pick a client: MCP server for Claude/Cursor, Agent Zero for autonomous replies, or n8n for visual workflows.
- Bookmark the API Reference — every endpoint, every status code.