Skip to content

Curl Cookbook

Copy-paste HTTP commands for every Privitty Edge operation.
Replace host, chat IDs, and paths as needed.

Base URL: http://127.0.0.1:7200
Account ID: always 1

Set a helper to reduce repetition:

RPC='curl -s -X POST http://127.0.0.1:7200/rpc -H Content-Type:application/json'

Health

curl -s http://127.0.0.1:7200/health | jq .

Status

# Account e-mail address
$RPC -d '{"jsonrpc":"2.0","method":"get_config","params":[1,"addr"],"id":1}'

# Display name
$RPC -d '{"jsonrpc":"2.0","method":"get_config","params":[1,"displayname"],"id":2}'

# Technical info
$RPC -d '{"jsonrpc":"2.0","method":"get_info","params":[1],"id":3}'

# Connectivity (0=unconfigured, 4000+=connected)
$RPC -d '{"jsonrpc":"2.0","method":"get_connectivity","params":[1],"id":4}'

# Peer count
$RPC -d '{"jsonrpc":"2.0","method":"get_contact_ids","params":[1,0,null],"id":5}'

Invite a peer

$RPC -d '{"jsonrpc":"2.0","method":"get_chat_securejoin_qr_code","params":[1,null],"id":1}'

Share the returned URL with the remote operator.


Join a peer (accept invite)

$RPC -d '{"jsonrpc":"2.0","method":"secure_join","params":[1,"OPENPGP4FPR:…"],"id":1}'

Or pass the https://i.privittytech.com/#… short link.


Send a text message

$RPC -d '{"jsonrpc":"2.0","method":"send_msg","params":[1,7,{"text":"Batch complete"}],"id":1}'
# 1. Lookup contact
$RPC -d '{"jsonrpc":"2.0","method":"lookup_contact_id_by_addr","params":[1,"ops@example.com"],"id":1}'

# 2. Create chat (use contact_id from step 1)
$RPC -d '{"jsonrpc":"2.0","method":"create_chat_by_contact_id","params":[1,12],"id":2}'

# 3. Send (use chat_id from step 2)
$RPC -d '{"jsonrpc":"2.0","method":"send_msg","params":[1,7,{"text":"Batch complete"}],"id":3}'

Send an encrypted file

The file must exist at a path inside the container (e.g. /data/report.pdf). See Make a file available in the container below.

Same as ./privitty-edge send-file on bare metal:

# Copy file in (if not using a bind mount)
docker cp ~/Desktop/env/environmental_monitoring.pdf privitty-edged:/data/

# Send
docker exec privitty-edged \
  privitty-edge --accounts /var/lib/privitty send-file \
  --chat 12 \
  --file /data/environmental_monitoring.pdf \
  --message "Sending a CDR file" \
  --allow-forward \
  --duration 40

Expected:

Sending 'environmental_monitoring.pdf' (0.0 MB) via Privitty …
  allow_download=false  allow_forward=true   duration=40 min
  Encrypted  → /data/environmental_monitoring.pdf.prv
  File ID    : 1
✓ File sent via SMTP  (msg_id=22)

Full walkthrough: Example C · Send a File.

curl (two RPC calls — no single combined method)

# 1. Get peer contact ID from chat
$RPC -d '{"jsonrpc":"2.0","method":"get_chat_contacts","params":[1,12],"id":1}'

# 2. Encrypt (peer_contact_id=12; allow_download=false, allow_forward=true, 40 min)
$RPC -d '{"jsonrpc":"2.0","method":"privitty_send_file","params":[1,12,12,"/data/environmental_monitoring.pdf",false,true,40],"id":2}'

# 3. Deliver .prv attachment (use encrypted_path from step 2)
$RPC -d '{"jsonrpc":"2.0","method":"send_msg","params":[1,12,{"text":"Sending a CDR file","file":"/data/environmental_monitoring.pdf.prv","viewtype":"File"}],"id":3}'

One-liner (encrypt + send):

CHAT=12
FILE="/data/environmental_monitoring.pdf"
MSG="Sending a CDR file"

PEER=$(curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d "{\"jsonrpc\":\"2.0\",\"method\":\"get_chat_contacts\",\"params\":[1,${CHAT}],\"id\":1}" \
  | jq '[.result[] | select(. != 1)][0]')

PRV=$(curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d "{\"jsonrpc\":\"2.0\",\"method\":\"privitty_send_file\",\"params\":[1,${CHAT},${PEER},\"${FILE}\",false,true,40],\"id\":2}" \
  | jq -r '.result.encrypted_path')

curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d "{\"jsonrpc\":\"2.0\",\"method\":\"send_msg\",\"params\":[1,${CHAT},{\"text\":\"${MSG}\",\"file\":\"${PRV}\",\"viewtype\":\"File\"}],\"id\":3}" \
  | jq .

License required

File send requires an active license. See License Management.


Make a file available in the container

Method When to use Host → container
Bind mount Production; files dropped by SCADA/MES -v ~/privitty-outbox:/data at docker run
docker cp Quick test; container already running docker cp file.pdf privitty-edged:/data/
Volume staging No bind mount; no recreate Copy into privitty-data via Alpine sidecar

Bind mount — set at container start only:

mkdir -p ~/privitty-outbox
docker run -d ... -v privitty-data:/var/lib/privitty -v ~/privitty-outbox:/data privitty/edge:latest
cp ~/Desktop/report.pdf ~/privitty-outbox/    # appears as /data/report.pdf

docker cp — works on a running container:

docker cp ~/Desktop/env/environmental_monitoring.pdf privitty-edged:/data/

Warning

A bind mount only works if you included -v host/path:/data when the container was created. Adding it later requires recreating the container (the privitty-data volume is kept).

Details: Example C · Send a File.


List chats

The CLI command privitty-edge chats prints:

    ID  T  Name                                  Unread
──────────────────────────────────────────────────────────────
     3  @  factory-gw-01
     7  #  ops-team                              (2 new)
    12  @  sensor-node-02
    15  #  audit-log                             (1 new)

Reproduce the same information with curl using this two-call sequence:

Step 1 — Get all chat IDs:

curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"get_chatlist_entries","params":[1,null,null,null],"id":1}'
{"jsonrpc":"2.0","result":[3,7,12,15],"id":1}

Step 2 — Get name and type for those IDs:

curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"get_chatlist_items_by_entries","params":[1,[3,7,12,15]],"id":2}' \
  | jq '.result | to_entries | sort_by(.key | tonumber)
      | map({
          id:     (.key | tonumber),
          type:   (if .value.chatType == 120 then "group"
                   elif .value.chatType == 130 then "broadcast"
                   else "1:1" end),
          name:   .value.name,
          unread: .value.freshMessageCounter
        })'

Output matches the CLI table:

[
  {"id":  3, "type": "1:1",   "name": "factory-gw-01",  "unread": 0},
  {"id":  7, "type": "group", "name": "ops-team",        "unread": 2},
  {"id": 12, "type": "1:1",   "name": "sensor-node-02",  "unread": 0},
  {"id": 15, "type": "group", "name": "audit-log",       "unread": 1}
]
chatType CLI symbol JSON type
100 @ "1:1"
120 # "group"
130 "broadcast"

One-liner — chain both calls (requires jq):

IDS=$(curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"get_chatlist_entries","params":[1,null,null,null],"id":1}' \
  | jq '.result')

curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d "{\"jsonrpc\":\"2.0\",\"method\":\"get_chatlist_items_by_entries\",\"params\":[1,${IDS}],\"id\":2}" \
  | jq '.result | to_entries | sort_by(.key | tonumber)
      | map({id: (.key|tonumber), type: (if .value.chatType==120 then "group" elif .value.chatType==130 then "broadcast" else "1:1" end), name: .value.name, unread: .value.freshMessageCounter})'

Use the id values from this output as the chat_id in the next section.


List messages in a chat

The CLI command privitty-edge msgs <chat_id> prints:

══ 2026-05-27 ══════════════════════════════════════════════

 [   81]  10:02  sensor-node-02
           Batch started

 [   82]  10:05  sensor-node-02
           Sensor reading 42.7°C

 [   83]  10:08  Me  ✓✓
           🔒  report.csv.prv  (38 KB)
               → privitty-viewer --msg-id 83

 [   84]  10:10  Me  👁
           Batch complete

Reproduce the same information with curl using this two-call sequence:

Step 1 — Get message IDs for chat (replace 7 with your chat ID):

curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"get_message_ids","params":[1,7,false,false],"id":1}'
{"jsonrpc":"2.0","result":[81,82,83,84],"id":1}

Step 2 — Fetch message details (paste the IDs from Step 1):

curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"get_messages","params":[1,[81,82,83,84]],"id":2}' \
  | jq '.result | to_entries | sort_by(.key | tonumber)
      | map({
          msg_id:    (.key | tonumber),
          time:      (.value.timestamp | strftime("%H:%M") // "??:??"),
          from:      (if .value.fromId == 1 then "Me"
                      else (.value.sender.displayName // "?") end),
          state:     (if   .value.state == 1  then "unread"
                      elif .value.state == 10 then "pending"
                      elif .value.state == 20 then "sent ✓"
                      elif .value.state == 26 then "delivered ✓✓"
                      elif .value.state == 28 then "read 👁"
                      else "" end),
          text:      .value.text,
          file:      .value.file,
          file_size: .value.fileBytes,
          encrypted: (.value.file // "" | endswith(".prv"))
        })'

Output matches the CLI display:

[
  {
    "msg_id": 81,
    "time":   "10:02",
    "from":   "sensor-node-02",
    "state":  "read 👁",
    "text":   "Batch started",
    "file":   null,
    "file_size": 0,
    "encrypted": false
  },
  {
    "msg_id": 83,
    "time":   "10:08",
    "from":   "Me",
    "state":  "delivered ✓✓",
    "text":   "Report attached",
    "file":   "/var/lib/privitty/blobs/report.csv.prv",
    "file_size": 38912,
    "encrypted": true
  }
]
state CLI symbol JSON state
Fresh / unread (none) "unread"
Pending "pending"
Delivered to server "sent ✓"
Delivered to device ✓✓ "delivered ✓✓"
Read 👁 "read 👁"

One-liner — chain both calls for a given chat ID (replace 7):

MSGIDS=$(curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"get_message_ids","params":[1,7,false,false],"id":1}' \
  | jq '.result')

curl -s -X POST http://127.0.0.1:7200/rpc \
  -H "Content-Type: application/json" \
  -d "{\"jsonrpc\":\"2.0\",\"method\":\"get_messages\",\"params\":[1,${MSGIDS}],\"id\":2}" \
  | jq '.result | to_entries | sort_by(.key|tonumber)
      | map({msg_id:(.key|tonumber), from:(if .value.fromId==1 then "Me" else (.value.sender.displayName//"?") end), state:.value.state, text:.value.text, encrypted:(.value.file//""|endswith(".prv"))})'

View an encrypted file

Any message where "encrypted": true can be opened directly with:

docker exec -it privitty-edged privitty-viewer --msg-id 83
Requires the privitty/edge:latest-viewer image. The file is decrypted in memory — no plaintext ever touches the filesystem.


Revoke file access

# 1. Load message to get .prv path
$RPC -d '{"jsonrpc":"2.0","method":"get_messages","params":[1,[42]],"id":1}'

# 2. Resolve file ID (use file path from message)
$RPC -d '{"jsonrpc":"2.0","method":"privitty_get_file_id_by_path","params":[1,"/path/to/file.prv"],"id":2}'

# 3. Revoke (chat_id=7, file_id=42, peer_contact_id=12)
$RPC -d '{"jsonrpc":"2.0","method":"privitty_revoke_file_access","params":[1,7,42,12],"id":3}'

SSE event stream

curl -N http://127.0.0.1:7200/events

See Example D · SSE Streaming for filtered Python/shell clients.


License (via CLI inside container)

docker exec privitty-edged \
  privitty-edge --accounts /var/lib/privitty license status

docker exec privitty-edged \
  privitty-edge --accounts /var/lib/privitty \
  license activate "https://plm.privittytech.com/v1/license/TOKEN"

See License Management.


End-to-end examples

Walkthrough Link
A · First boot examples/first-boot.md
B · Invite & send examples/invite-and-send.md
C · Send a file examples/send-file.md
D · SSE streaming examples/sse-streaming.md
E · License examples/license.md