Complete API documentation for AccountSafe.
Base URL: /api/
AccountSafe uses zero-knowledge authentication. The client derives an authentication hash from the master password and transmits only the hash, never the password itself.
Create a new user account.
POST /api/zk/register/
Request Body:
| Field | Type | Description |
|---|---|---|
username |
string | Unique username |
email |
string | Email address |
auth_hash |
string | Argon2id-derived authentication hash |
salt |
string | Base64-encoded salt for key derivation |
turnstile_token |
string | Cloudflare Turnstile token (if enabled) |
Response: 201 Created
{
"id": 1,
"username": "user",
"email": "user@example.com"
}Authenticate and receive JWT tokens.
POST /api/zk/login/
Request Body:
| Field | Type | Description |
|---|---|---|
username |
string | Username |
auth_hash |
string | Argon2id-derived authentication hash |
turnstile_token |
string | Cloudflare Turnstile token (if enabled) |
Response: 200 OK
{
"key": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"salt": "base64-encoded-salt",
"is_duress": false
}Invalidate the current session.
POST /api/zk/logout/
Authorization: Token <jwt>
Response: 200 OK
Obtain a new access token using refresh token.
POST /api/token/refresh/
Request Body:
| Field | Type | Description |
|---|---|---|
refresh |
string | Refresh token |
Response: 200 OK
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}All vault data is encrypted client-side before transmission. The server stores and returns encrypted blobs without the ability to decrypt them.
Organizational containers for credentials.
GET /api/categories/
Authorization: Token <jwt>
Response: 200 OK
[
{
"id": 1,
"name": "Social Media",
"icon": "users",
"order": 0
}
]POST /api/categories/
Authorization: Token <jwt>
Content-Type: application/json
Request Body:
| Field | Type | Description |
|---|---|---|
name |
string | Category name |
icon |
string | Icon identifier (optional) |
Response: 201 Created
PUT /api/categories/{id}/
Authorization: Token <jwt>
DELETE /api/categories/{id}/
Authorization: Token <jwt>
Response: 204 No Content
Service/platform groupings within categories.
GET /api/organizations/
Authorization: Token <jwt>
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
category |
integer | Filter by category ID |
Response: 200 OK
[
{
"id": 1,
"name": "GitHub",
"logo_url": "https://...",
"website_link": "https://github.com",
"category": 1
}
]POST /api/organizations/
Authorization: Token <jwt>
PUT /api/organizations/{id}/
DELETE /api/organizations/{id}/
Authorization: Token <jwt>
Encrypted credential storage.
GET /api/profiles/
Authorization: Token <jwt>
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
organization |
integer | Filter by organization ID |
Response: 200 OK
[
{
"id": 1,
"title": "Personal Account",
"organization": 1,
"username_encrypted": "base64...",
"username_iv": "base64...",
"password_encrypted": "base64...",
"password_iv": "base64...",
"email_encrypted": "base64...",
"email_iv": "base64...",
"notes_encrypted": "base64...",
"notes_iv": "base64...",
"is_pinned": false,
"is_breached": false,
"password_strength": 4,
"created_at": "2025-01-01T00:00:00Z"
}
]POST /api/profiles/
Authorization: Token <jwt>
Content-Type: application/json
Request Body:
| Field | Type | Description |
|---|---|---|
title |
string | Display name |
organization |
integer | Organization ID |
username_encrypted |
string | AES-GCM encrypted username |
username_iv |
string | Initialization vector |
password_encrypted |
string | AES-GCM encrypted password |
password_iv |
string | Initialization vector |
email_encrypted |
string | AES-GCM encrypted email (optional) |
email_iv |
string | Initialization vector |
notes_encrypted |
string | AES-GCM encrypted notes (optional) |
notes_iv |
string | Initialization vector |
Response: 201 Created
PUT /api/profiles/{id}/
Authorization: Token <jwt>
DELETE /api/profiles/{id}/
Authorization: Token <jwt>
Response: 204 No Content
POST /api/profiles/{id}/toggle-pin/
Authorization: Token <jwt>
Secondary authentication for sensitive operations.
POST /api/pin/set/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
pin |
string | 4-6 digit PIN |
Response: 200 OK
POST /api/pin/verify/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
pin |
string | PIN to verify |
Response: 200 OK
{
"verified": true
}GET /api/sessions/
Authorization: Token <jwt>
Response: 200 OK
[
{
"id": "abc123",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0...",
"location": "New York, US",
"created_at": "2025-01-01T00:00:00Z",
"is_current": true
}
]POST /api/sessions/{id}/revoke/
Authorization: Token <jwt>
Response: 200 OK
Duress Mode allows you to set up a secondary password that reveals a fake vault with decoy credentials. This protects your real vault under coercion.
POST /api/zk/set-duress/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
master_auth_hash |
string | Current master password auth hash (for verification) |
duress_auth_hash |
string | Duress password auth hash |
duress_salt |
string | Salt for duress key derivation |
sos_email |
string | Optional: Email to notify on duress login |
Response: 200 OK
{
"message": "Duress mode configured successfully"
}POST /api/zk/clear-duress/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
auth_hash |
string | Master password auth hash (for verification) |
Response: 200 OK
Login with duress credentials returns a valid token but serves fake vault data.
POST /api/zk/switch-mode/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
auth_hash |
string | Duress authentication hash |
Response: 200 OK
{
"message": "Mode switched successfully",
"is_duress": true
}Canary Traps are decoy credentials that trigger alerts when accessed. Use them to detect unauthorized access to your exported data.
GET /api/security/canary-traps/
Authorization: Token <jwt>
Response: 200 OK
[
{
"id": 1,
"service_name": "FakeBank Login",
"trigger_email": "user@example.com",
"created_at": "2025-01-01T00:00:00Z",
"last_triggered_at": null,
"trigger_count": 0
}
]POST /api/security/canary-traps/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
service_name |
string | Descriptive name for the canary |
trigger_email |
string | Email to notify on trigger |
canary_username |
string | Fake username to distribute |
canary_password_hash |
string | Hashed fake password |
Response: 201 Created
This endpoint is called when a canary credential is used. It logs the attempt and sends alerts.
POST /api/security/canary-traps/trigger/
Request Body:
| Field | Type | Description |
|---|---|---|
canary_token |
string | The canary identifier token |
Response: 200 OK (always succeeds to avoid detection)
DELETE /api/security/canary-traps/{id}/
Authorization: Token <jwt>
Response: 204 No Content
Export and import encrypted vault backups. The server never sees decrypted data.
GET /api/zk/vault/export/
Authorization: Token <jwt>
Response: 200 OK
{
"encrypted_vault": "base64-encoded-encrypted-blob",
"salt": "base64-encoded-salt",
"version": 1,
"exported_at": "2025-01-01T00:00:00Z"
}POST /api/zk/vault/import/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
encrypted_vault |
string | Previously exported encrypted blob |
auth_hash |
string | Auth hash for verification |
Response: 200 OK
{
"message": "Vault imported successfully",
"items_imported": 42
}Time-limited secret sharing.
POST /api/shared-secrets/
Authorization: Token <jwt>
Request Body:
| Field | Type | Description |
|---|---|---|
content_encrypted |
string | Encrypted content |
content_iv |
string | Initialization vector |
expires_in |
integer | Expiration in hours |
passphrase_hash |
string | Optional passphrase protection |
Response: 201 Created
{
"id": "uuid",
"url": "https://domain.com/share/uuid",
"expires_at": "2025-01-02T00:00:00Z"
}GET /api/shared-secrets/{id}/
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
passphrase |
string | Passphrase if protected |
Response: 200 OK
{
"content_encrypted": "base64...",
"content_iv": "base64..."
}GET /api/user/
Authorization: Token <jwt>
Response: 200 OK
{
"id": 1,
"username": "user",
"email": "user@example.com"
}GET /api/dashboard/statistics/
Authorization: Token <jwt>
Response: 200 OK
{
"total_credentials": 42,
"total_categories": 5,
"total_organizations": 12,
"breached_count": 1,
"weak_passwords": 3,
"health_score": 85
}All errors follow a consistent format:
{
"error": "Error message",
"detail": "Additional details (optional)"
}| Code | Description |
|---|---|
400 |
Bad Request - Invalid input |
401 |
Unauthorized - Invalid or missing token |
403 |
Forbidden - Insufficient permissions |
404 |
Not Found - Resource does not exist |
429 |
Too Many Requests - Rate limit exceeded |
500 |
Internal Server Error |