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.
Last updated: