/api/qmail/net/messages/upload
GETUpload email content to QMail servers from Client1. This page includes one-click GET examples for both body mode and file mode.
Description
The /api/qmail/net/messages/upload endpoint supports two modes: body mode, which CBDF-encodes the email in memory, and email_file mode, which uploads a prebuilt .qmail file from disk. The response is asynchronous and returns a task_id immediately.
All files in a single upload share the same File GUID. The server distinguishes them by the file_type byte, which is inferred from the file extension.
After uploading the files, the API automatically issues the Tell command, which sends information about the email to each receiver's Beacon server. This ensures that the recipient is instantly notified of the incoming message without needing to poll for new mail.
If you omit wallet and wallet_path, resolve_wallet_path() defaults to the Default wallet. That is the recommended doc/test setup for Client1.
Use repeated to=, cc=, and bcc= parameters for multiple recipients. Legacy comma/semicolon-separated strings are still accepted for backward compatibility.
The upload flow accepts up to 50 recipients per field. If the overall request contains too many repeated parameters, the HTTP layer now rejects it with HTTP 413 Payload Too Large and { "error": true, "message": "Too many request parameters", "code": 413 } instead of silently dropping extras.
Parameters
Send parameters as form data or query string values:
| Parameter | Type | Required | Description |
|---|---|---|---|
to |
string | Required for body mode | Repeat this parameter for each recipient. Example: to=Mohsin.Mehraj@FullStack#FVH.giga&to=Sean.Worthington@Founder#FUV.giga. |
email_file |
string | Required for file mode | Absolute path to the binary email file on the local filesystem. |
body |
string | Required for body mode | Raw email body. When body is used, to is also required. |
duration |
integer | No | Number of weeks to store the data on QMail servers. Minimum effective value is 1. Default: 4. |
cc |
string | No | Optional repeated CC parameter. Legacy comma/semicolon-separated strings still work. |
bcc |
string | No | Optional repeated BCC parameter. Legacy comma/semicolon-separated strings still work. |
attachments |
string | No | Comma-separated absolute file paths for attachments. |
wallet_path |
string | No | Optional wallet selector. If omitted, the API uses the Default wallet through resolve_wallet_path(). |
Server-Side Orchestration
The API performs the following steps automatically:
- Validate inputs — Check all required parameters, verify files exist and are readable, parse QMail addresses to binary.
- Calculate costs — Storage cost (1 CC per server per file) + inbox fees (10 CC per recipient). For example: 1 email + 2 attachments to 3 recipients = 15 CC storage + 30 CC inbox = 45 CC total.
- Check balance — Verify the wallet has sufficient funds. Returns an error if the balance is too low.
- Generate File GUID — A single 16-byte GUID shared by all files in this upload.
- Acquire locker keys — Generate a unique 16-byte locker code for each of the 5 QMail servers.
- Upload loop — For each file (email body first, then attachments):
- Read file into memory
- Split into 4 data stripes + 1 parity stripe (Reed-Solomon)
- Upload all 5 stripes in parallel to their assigned servers (CMD 70 UPLOAD2)
- Require at least 4 of 5 uploads to succeed
- Send TELL2 — Notify the beacon server (CMD 71 TELL2) with recipient list, server locations, and locker codes so recipients can find and download the email.
The file type byte in the upload header identifies the content type of each file:
| Type ID | Name | Description |
|---|---|---|
| 0 | QMAIL | Primary email message file |
| 1 | QTEXT | Reserved for future text processing |
| 2 | QCHAT | Reserved for future chat functionality |
| 3 | PEER_TO_PEER_SECRET_CBDF | Private user identification data |
| 4 | GROUPS_SECRET_CBDF | Private group identification data |
| 5 | QPACKET | Reserved for future packet management |
| 6 | QDATA | File management for QData servers |
| 10-255 | Attachment N | File attachments (10 = first attachment, etc.) |
Response
Returns a JSON object immediately indicating that the asynchronous upload task has started. Poll /api/system/tasks with the returned task_id for completion details.
Success Response Properties
"qmail-upload".true when the task was created successfully./api/system/tasks./api/system/tasks?task_id={task_id} on the same host. Provided so clients do not need to build the URL themselves.Example Success Response
{
"command": "qmail-upload",
"success": true,
"task_id": "Apr-18-26_08-15-00-AM-afce",
"url": "http://localhost:8080/api/system/tasks?task_id=Apr-18-26_08-15-00-AM-afce",
"source": "cbdf-from-body",
"to": "Mohsin.Mehraj@FullStack#FVH.giga",
"duration": 4,
"tell_enabled": true,
"message": "Upload+Tell started - poll /api/system/tasks for status"
}
Error Response Properties
true on synchronous validation/startup failures.Example Error Responses
// Missing required parameter
{
"error": true,
"message": "Missing required parameter: body or email_file",
"code": 400
}
// Email file not found
{
"error": true,
"message": "email_file not found or not readable",
"code": 400
}
// Too many repeated request parameters
{
"error": true,
"message": "Too many request parameters",
"code": 413
}
Try It Out
Examples
cURL
# One-click body mode test
curl "http://localhost:8081/api/qmail/net/messages/upload?body=QMail%20upload%20body-mode%20doc%20test%20from%20Client1.&to=Mohsin.Mehraj%40FullStack%23FVH.giga&subject=Upload%20Doc%20Test&duration=4"
# One-click file mode test
curl "http://localhost:8081/api/qmail/net/messages/upload?email_file=E%3A%5CClient1%5CClient_Data%5CWallets%5CMail%5COutgoing%5Ctest1.qmail&attachments=E%3A%5CClient1%5CClient_Data%5CWallets%5CMail%5COutgoing%5Cattachment1.pdf%2CE%3A%5CClient1%5CClient_Data%5CWallets%5CMail%5COutgoing%5Cattachment2.pdf&to=Mohsin.Mehraj%40FullStack%23FVH.giga&subject=Upload%20File%20Test&duration=4"
# POST body mode
curl -X POST "http://localhost:8081/api/qmail/net/messages/upload" \
-d "to=Mohsin.Mehraj%40FullStack%23FVH.giga" \
-d "to=Sean.Worthington%40Founder%23FUV.giga" \
-d "body=QMail upload body-mode doc test from Client1." \
-d "subject=Upload Doc Test" \
-d "duration=4"
JavaScript (fetch)
const API_BASE = 'http://localhost:8081/api';
async function uploadEmail() {
const params = new URLSearchParams();
[
'Mohsin.Mehraj@FullStack#FVH.giga',
'Sean.Worthington@Founder#FUV.giga'
].forEach(value => params.append('to', value));
params.append('email_file', 'E:\\Client1\\Client_Data\\Wallets\\Mail\\Outgoing\\test1.qmail');
params.append('attachments', 'E:\\Client1\\Client_Data\\Wallets\\Mail\\Outgoing\\attachment1.pdf,E:\\Client1\\Client_Data\\Wallets\\Mail\\Outgoing\\attachment2.pdf');
params.append('body', 'QMail upload body-mode doc test from Client1.');
params.append('duration', '4');
try {
const response = await fetch(`${API_BASE}/qmail/net/messages/upload`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString()
});
const result = await response.json();
if (response.ok && result.success) {
console.log('Upload task started!');
console.log('Task ID:', result.task_id);
console.log('Message:', result.message);
} else {
console.error('Upload failed:', result.message);
console.error('HTTP code:', result.code ?? response.status);
}
} catch (error) {
console.error('Error:', error);
}
}
uploadEmail();
Python
import requests
API_BASE = 'http://localhost:8081/api'
def upload_email():
params = [
('to', 'Mohsin.Mehraj@FullStack#FVH.giga'),
('to', 'Sean.Worthington@Founder#FUV.giga'),
('email_file', r'E:\\Client1\\Client_Data\\Wallets\\Mail\\Outgoing\\test1.qmail'),
('attachments', r'E:\\Client1\\Client_Data\\Wallets\\Mail\\Outgoing\\attachment1.pdf,E:\\Client1\\Client_Data\\Wallets\\Mail\\Outgoing\\attachment2.pdf'),
('body', 'QMail upload body-mode doc test from Client1.'),
('duration', 4)
]
try:
response = requests.post(f'{API_BASE}/qmail/net/messages/upload', data=params)
result = response.json()
if result.get('success'):
print('Upload task started!')
print(f'Task ID: {result["task_id"]}')
print(result['message'])
else:
print(f'Upload failed: {result["message"]}')
except Exception as e:
print(f'Error: {str(e)}')
upload_email()