Stores a new credential in the encrypted vault. The credential key is encrypted using Argon2id key derivation and libsodium secretbox.
Authentication
Requires a valid session token in the Authorization header:
Authorization: Bearer <session_token>
Request Body
Service identifier. For first-class providers: openai, anthropic, binance. For custom services: custom.<name>
Human-readable credential name. Use descriptive names like “primary”, “production”, or “backup”
The credential value (API key, token, or secret). Will be trimmed and encrypted before storage
Response
Returns the created credential metadata (the key value is NOT included).
Unique credential identifier (UUID)
Unix timestamp when created
Always null for newly created credentials
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"service": "openai",
"name": "primary",
"created_at": 1700000000,
"last_used_at": null
}
Error Responses
Validation Errors (400)
{
"error": "service, name, and key are required"
}
Returned when any required field is missing or contains only whitespace.
Status Codes
201 - Created successfully
400 - Bad request (missing or empty required fields)
401 - Unauthorized (missing or invalid session token)
500 - Internal server error (encryption or database failure)
Examples
curl -X POST https://localhost:3978/api/credentials \
-H "Authorization: Bearer fn_sess_abc123..." \
-H "Content-Type: application/json" \
-d '{
"service": "openai",
"name": "production",
"key": "sk-proj-..."
}'
Implementation Details
- Input values are trimmed before storage
- Keys are encrypted using libsodium
crypto_secretbox_easy with a random 24-byte nonce
- The master encryption key is derived from your master password using Argon2id with 256MB memory cost
- Each credential gets a unique UUID identifier
- Multiple credentials can exist for the same service with different names