IPC Endpoints
Complete reference for Flowsta Vault's local IPC API.
Flowsta Vault runs an HTTP server on 127.0.0.1:27777 that third-party apps use for identity linking, authentication, and backups. The @flowsta/holochain and @flowsta/auth-tauri SDKs wrap these endpoints, but you can call them directly if needed.
Base URL
http://127.0.0.1:27777Vault tries ports 27777, 27778, 27779 in sequence if the previous port is in use.
Status
GET /status
Check if Vault is running and unlocked.
Response:
{
"unlocked": true,
"agent_pub_key": "uhCAk7JpEWfkiV...",
"did": "did:flowsta:uhCAk...",
"version": "0.1.0",
"display_name": "John Doe",
"username": "johndoe",
"profile_picture": "data:image/svg+xml;base64,..."
}TIP
If the request fails to connect, Vault is not running. The running field is not part of the response — a successful response means Vault is running.
Scope enforcement
Profile fields (display_name, username, profile_picture) are only returned if your app was granted those scopes by the user at link time. Fields for ungranted scopes are returned as null. Configure which scopes your app requests in your app settings at dev.flowsta.com.
The agent_pub_key and did fields are always present when the Vault is unlocked — they are not scope-gated.
Agent Linking
POST /link-identity
Request an identity link. Shows an approval dialog in Vault.
Request:
{
"app_name": "ChessChain",
"client_id": "flowsta_app_abc123",
"app_agent_pub_key": "uhCAk..."
}Response (on approval):
{
"success": true,
"vault_agent_pub_key": "uhCAk...",
"vault_signature": "base64-encoded-signature"
}Response (on denial):
{
"success": false,
"error": "user_denied"
}POST /revoke-identity
Notify Vault that an identity link has been revoked.
Request:
{
"app_name": "ChessChain",
"app_agent_pub_key": "uhCAk..."
}GET /link-status
Check if a specific agent is still linked.
Query parameters: client_id, app_agent_pub_key
GET /link-status?client_id=flowsta_app_abc123&app_agent_pub_key=uhCAk...Response:
{
"linked": true,
"app_name": "ChessChain"
}Authentication (Tauri Apps)
POST /authenticate
Authenticate a Tauri desktop app. Shows an approval dialog in Vault with a 60-second timeout.
Request:
{
"app_name": "Your Desktop App",
"client_id": "flowsta_app_abc123",
"challenge": "optional-challenge-to-sign",
"reason": "Login to Your Desktop App"
}The client_id and challenge fields are optional. The reason field is shown in the approval dialog.
Response:
{
"success": true,
"did": "did:flowsta:uhCAk...",
"agent_pub_key": "uhCAk...",
"signature": "base64-encoded-signature",
"signed_at": 1709251200
}The signature and signed_at fields are only present if a challenge was provided.
Backups
POST /backup
Store app data in the Vault's encrypted local storage. Works while the Vault is locked (after first unlock in session).
Each call without a label creates a new timestamped snapshot (up to 10 per app, oldest auto-rotated). Pass an explicit label to overwrite a named backup.
Request:
{
"client_id": "flowsta_app_abc123",
"app_name": "ChessChain",
"content_type": "application/json",
"data": {
"polls_created": { "count": 2, "polls": [...] },
"votes_cast": { "count": 5, "votes": [...] },
"private_data": {
"_readme": "Decrypted from encrypted DHT entries.",
"vote_rationales": { "count": 1, "rationales": [...] },
"drafts": { "count": 0, "drafts": [] }
}
}
}Human-readable backups
Include _readme fields, use human-readable names (poll_title not just hashes), and decrypt any encrypted entries before including them. The Vault encrypts the backup at rest — no need to double-encrypt.
Use the canonical payload shape
When data follows the canonical v1 backup shape (top-level version: 1 + cells[].records[] with human_readable + raw_record per record), the Vault lights up extra UI: per-entry-type counts on the Your Data page ("12 polls, 38 votes"), and the user's CAL §4.2.1 data export inlines the readable view of each record instead of raw bytes. Any other JSON shape still works, but you don't get those features.
Response:
{
"success": true,
"label": "backup-1774321234",
"data_size": 1024,
"created_at": 1774321234
}GET /backup/list
List all backups stored in Vault.
Response:
{
"app_count": 1,
"total_backups": 3,
"total_size": 4096,
"apps": [
{
"client_id": "flowsta_app_abc123",
"app_name": "ChessChain",
"backup_count": 3,
"total_size": 4096,
"last_backup_at": 1709251200,
"latest_summary": {
"counts_by_entry_type": { "Game": 12, "Move": 84 },
"total_records": 96
}
}
]
}The latest_summary field is populated when the most recent backup follows the canonical v1 shape. It's null for older / non-canonical backups.
POST /backup/retrieve
Retrieve a stored backup. Omit label to get the most recent snapshot.
Request:
{
"client_id": "flowsta_app_abc123",
"label": "backup-1774321234"
}POST /backup/delete
Delete a stored backup.
Request:
{
"client_id": "flowsta_app_abc123",
"label": "latest"
}Canonical backup payload (v1)
When you post a backup whose data follows this shape, the Vault recognises it and unlocks per-entry-type summaries on the Your Data page plus human-readable inlining in the user's CAL §4.2.1 data export.
{
"version": 1,
"_readme": "Your YourApp data, backed up automatically by Flowsta Vault…",
"license": "Cryptographic Autonomy License v1.0 (CAL-1.0)",
"app": { "name": "YourApp", "client_id": "flowsta_app_…" },
"agent_pub_key": "uhCAk…",
"exported_at_iso": "2026-05-29T03:42:11Z",
"_summary": {
"countsByEntryType": { "Game": 12, "Move": 84 },
"totalRecords": 96
},
"cells": [
{
"role_name": "games",
"_readme": "Each record below is one thing you did…",
"records": [
{
"entryType": "Game",
"actionHash": "uhCkk…",
"createdAtMs": 1716969780000,
"human_readable": {
"name": "Friday Night Chess",
"opponent": "uhCAk…",
"started_at": 1716969780000
},
"raw_record": {
"entry_b64": "<base64-encoded MessagePack bytes>",
"action_address": "uhCkk…",
"action_type": "Create"
}
}
]
}
]
}Two views per record, both required for canonical-shape recognition:
human_readable— the decoded entry as plain JSON. This is what the user sees in their downloadable CAL data export.raw_record— at minimum,entry_b64(the entry's MessagePack bytes, base64-encoded). This is whatrestoreFromVaulthands to yourrestore_recorddispatcher when the user reinstalls.
Optional top-level fields are preserved verbatim through the export pipeline. Apps that generate an independent Holochain agent key not derived from the Flowsta seed can add an app_keys block at the top level so their data export remains self-sufficient:
{
"version": 1,
…
"app_keys": {
"_readme": "The cryptographic seed YourApp generates locally for its own DNA.",
"device_seed_hex": "abc123…"
}
}The SDK's dumpCellStateForBackup() builds this shape from an AdminWebsocket instance. For apps whose frontend only has an AppWebsocket, generate it from a Tauri command using zome queries — see ProofPoll's build_canonical_backup for a worked example.
Signing
POST /sign
Sign data with the Vault's agent key. Requires user approval.
Request:
{
"type": "bytes",
"bytes": "base64-encoded-data",
"reason": "Sign this document"
}The type field is required and must be "bytes" or "action". The reason field is shown in the approval dialog.
Response:
{
"success": true,
"signature": "base64-encoded-ed25519-signature",
"agent_pub_key": "uhCAk..."
}Error Responses
All endpoints return errors in this format:
{
"success": false,
"error": "error_code",
"message": "Human-readable description"
}| Error Code | HTTP Status | Description |
|---|---|---|
vault_locked | 403 | Vault is locked |
user_denied | 403 | User rejected the request |
invalid_client_id | 400 | Client ID not registered |
missing_client_id | 400 | No client_id provided |
app_not_found | 404 | App ID not installed in Vault |
api_unreachable | 502 | Cannot reach Flowsta API for verification |
Next Steps
- @flowsta/holochain SDK - SDK that wraps these endpoints
- @flowsta/auth-tauri SDK - Desktop auth SDK
- Agent Linking - Identity attestation guide
- Building Holochain Apps - Integration guide