Sharing API Endpoints
Create, retrieve, and consume encrypted ephemeral burn-on-read secret shares via the REST API.
Sharing API Endpoints
The Sharing API enables programmatic creation and consumption of encrypted ephemeral secrets (Protected Links). Secrets are encrypted client-side before submission—the server stores only opaque ciphertext blobs and never possesses decryption keys.
Note
Recipients do not need a Vertueal account to consume a shared secret. The decryption key is transmitted out-of-band via the URL anchor fragment (#key), which browsers never send to the server.
1. Create an Ephemeral Share
POST /api/share
Stores a client-side encrypted secret payload on the server and returns a unique shareId. The client is responsible for encrypting the plaintext secret using AES-256-GCM before calling this endpoint.
Rate Limit
| Window | Maximum Requests | Scope |
|---|---|---|
| 60 seconds | 5 | Per IP address |
Request Headers
Content-Type: application/jsonRequest Body Schema
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
data_blob | string | Yes | — | Base64-encoded AES-256-GCM encrypted secret payload. |
meta_blob | string | No | null | Optional Base64-encoded encrypted metadata (e.g., label, sender name). |
iv | string | Yes | — | Base64-encoded initialization vector used during AES-GCM encryption. |
one_time | boolean | No | true | If true, the share is atomically deleted from the database upon first retrieval (burn-on-read). |
expires_in_hours | number | No | 24 | Time-to-live in hours before the share expires. Maximum: 168 (7 days). |
Code Examples
curl -X POST "https://passwordmanager.vertueal.com/api/share" \
-H "Content-Type: application/json" \
-d '{
"data_blob": "U2FsdGVkX1+vupppZksvRf5pq5g5XjFRl...",
"meta_blob": "U2FsdGVkX1+abc123metadata...",
"iv": "YWJjZGVmMTIzNDU2Nzg5MA==",
"one_time": true,
"expires_in_hours": 24
}'const response = await fetch("https://passwordmanager.vertueal.com/api/share", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data_blob: encryptedPayload, // AES-256-GCM ciphertext (Base64)
meta_blob: encryptedMeta, // Optional encrypted label
iv: initializationVector, // Base64-encoded IV
one_time: true,
expires_in_hours: 24,
}),
});
const { shareId, expiresAt } = await response.json();
// Construct the full protected link with out-of-band decryption key
const protectedLink = `https://passwordmanager.vertueal.com/share/${shareId}#${ephemeralKeyHex}`;import requests
response = requests.post(
"https://passwordmanager.vertueal.com/api/share",
json={
"data_blob": encrypted_payload, # AES-256-GCM ciphertext (Base64)
"meta_blob": encrypted_meta, # Optional encrypted label
"iv": initialization_vector, # Base64-encoded IV
"one_time": True,
"expires_in_hours": 24,
},
)
data = response.json()
share_id = data["shareId"]
protected_link = f"https://passwordmanager.vertueal.com/share/{share_id}#{ephemeral_key_hex}"Success Response (200 OK)
{
"success": true,
"shareId": "550e8400-e29b-41d4-a716-446655440000",
"expiresAt": "2026-09-21T18:30:00.000Z"
}Error Responses
| Status | Body | Cause |
|---|---|---|
400 | { "error": "Invalid payload" } | Missing required data_blob or iv fields. |
429 | { "error": "Too many requests" } | Exceeded 5 share creations per 60-second window. |
500 | { "error": "Share creation failed" } | Internal server error during database write. |
2. Retrieve (and Consume) an Ephemeral Share
GET /api/share
Fetches the encrypted payload for a given share. If the share was created with one_time: true, the record is atomically deleted from the database immediately upon retrieval (burn-on-read).
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | The unique shareId returned during creation. |
Code Examples
curl -X GET "https://passwordmanager.vertueal.com/api/share?id=550e8400-e29b-41d4-a716-446655440000"const shareId = "550e8400-e29b-41d4-a716-446655440000";
const response = await fetch(
`https://passwordmanager.vertueal.com/api/share?id=${shareId}`
);
const { data_blob, meta_blob, iv, created_at } = await response.json();
// Extract the ephemeral key from the URL anchor fragment
const ephemeralKey = window.location.hash.substring(1);
// Decrypt data_blob using AES-256-GCM with ephemeralKey and iv
const plaintext = await decryptWithAESGCM(data_blob, ephemeralKey, iv);import requests
share_id = "550e8400-e29b-41d4-a716-446655440000"
response = requests.get(
"https://passwordmanager.vertueal.com/api/share",
params={"id": share_id},
)
data = response.json()
# Decrypt data["data_blob"] using AES-256-GCM with the ephemeral key and data["iv"]Success Response (200 OK)
{
"data_blob": "U2FsdGVkX1+vupppZksvRf5pq5g5XjFRl...",
"meta_blob": "U2FsdGVkX1+abc123metadata...",
"iv": "YWJjZGVmMTIzNDU2Nzg5MA==",
"created_at": "2026-09-20T18:30:00.000Z"
}Warning
If the share was created with one_time: true, this response is returned exactly once. Any subsequent request for the same shareId returns 404 Not Found. The secret is permanently and irrevocably deleted from the database.
Error Responses
| Status | Body | Cause |
|---|---|---|
400 | { "error": "Missing ID" } | The id query parameter was not provided. |
404 | { "error": "Link not found or expired" } | Share does not exist, has already been consumed (burn-on-read), or has expired via TTL. |
500 | { "error": "Retrieval failed" } | Internal server error during database query. |
End-to-End Sharing Flow
The complete lifecycle of a Protected Link from creation to consumption:
Tip
The ephemeral decryption key (K) is embedded in the URL anchor fragment (#...). Per RFC 3986, browsers never transmit anchor fragments in HTTP requests. The server therefore never possesses the key material required to decrypt the shared secret.