/api/qmail/upload
GETUpload email and attachments to QMail servers with RAID-5 striped delivery across 3 to 32 QMail servers.
Description
The /api/qmail/upload endpoint uploads one or more files (email body + optional attachments) to the QMail network using the V2 protocol. Each file is split into 2 or more data stripes + 1 parity stripe using Reed-Solomon encoding, then each stripe is uploaded to a different QMail server in parallel.
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.
The caller (GUI) must build the binary .qmail email file and provide its absolute path. Attachments are provided as separate file paths. The API handles all server-side work: cost calculation, locker key acquisition, striping, parallel uploads, and recipient notification.
QMail addresses use a # separator: Jerry.Doe@myOrg#FUV.Giga. Internally, each address is a 7-byte binary value: coin_id(2) + denomination(1) + serial_number(4). The API resolves string addresses to their binary form using the contacts database.
Parameters
Send parameters as form data or query string values:
| Parameter | Type | Required | Description |
|---|---|---|---|
sender |
integer | No | Qmail addressed used by the sender (Optional and for future use). By default, the api will uses your only one Qmail address. |
To |
string | Yes | Simular to how PHP accepts array with multiple keys of the same name: e.g.
to=billy.jenkins@hotdog#8JD.Kilo&to=sara.Jones@viva#SE5.Mega
. |
email_file |
string | Yes | Absolute path to the binary email file on the local filesystem (e.g., email_file=E:\Client_Data\outbox\msg001.qmail. |
duration |
integer | No | Number of weeks to store the data on QMail servers. Range: 1–520. Default: Default is 8 months in Phase I. |
cc |
string | No | Simular to how PHP accepts array with multiple keys of the same name: e.g.
cc=billy.jenkins@hotdog#8JD.Kilo&cc=sara.Jones@viva#SE5.Mega
. |
bcc |
string | No | Simular to how PHP accepts array with multiple keys of the same name: e.g.
bc=billy.jenkins@hotdog#8JD.Kilo&bc=sara.Jones@viva#SE5.Mega
. |
attachments |
string | No | List of absolute file paths for attachments (e.g., attachment=E:\files\doc.pdf&attachement=D:\docs\my.md"). Each attachment is uploaded independently using the same File GUID. |
wallet_path |
string | No | Path to the wallet to use for payment. Defaults to the system default wallet. Otherwise wallet_path=C:\core\Client_Data\Wallets\Default\ |
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 indicating the result of the upload operation.
Success Response Properties
"success" when the upload completes successfully."upload".Example Success Response
{
"status": "success",
"operation": "upload",
"message": "Upload completed successfully",
"file_guid": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"files_uploaded": 3,
"total_cost_cc": 45,
"storage_cost_cc": 15,
"inbox_cost_cc": 30,
"upload_successes": 15,
"upload_failures": 0,
"tell_success": true,
"duration_weeks": 4
}
Error Response Properties
"fail" when the upload fails."upload".Example Error Responses
// Missing required parameter
{
"status": "fail",
"operation": "upload",
"error_code": 400,
"message": "Missing required parameter: to"
}
// Email file not found
{
"status": "fail",
"operation": "upload",
"error_code": 404,
"message": "Email file not found: E:\\outbox\\msg001.qmail"
}
// Insufficient balance
{
"status": "fail",
"operation": "upload",
"error_code": 402,
"message": "Insufficient balance: need 45 CC (15 storage + 30 inbox), wallet has 12 CC"
}
// Partial upload failure (not enough stripes succeeded)
{
"status": "fail",
"operation": "upload",
"error_code": 503,
"message": "Upload failed: only 2 of 5 stripes succeeded for attachment 1",
"file_guid": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"files_completed": 1,
"files_failed": 1
}
// TELL2 notification failed
{
"status": "fail",
"operation": "upload",
"error_code": 502,
"message": "Files uploaded but recipient notification failed. Recipients will not see this email.",
"file_guid": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"files_uploaded": 3
}
// Invalid recipient address
{
"status": "fail",
"operation": "upload",
"error_code": 422,
"message": "Invalid QMail address: billy.jenkins@hotdog. Missing domain separator '#'."
}
Try It Out
Examples
cURL
# Simple email (no attachments)
curl -X POST "http://localhost:8080/api/qmail/upload" \
-d "sender_id=16777216" \
-d "sender_denomination=1" \
-d "to=Sean%2316777216" \
-d "email_file=E%3A%5CClient_Data%5Coutbox%5Cmessage.qmail" \
-d "duration=4"
# Email with attachments and CC
curl -X POST "http://localhost:8080/api/qmail/upload" \
-d "sender_id=16777216" \
-d "sender_denomination=1" \
-d "to=Sean%2316777216,Bob%239876543" \
-d "cc=Alice%235555555" \
-d "email_file=E%3A%5CClient_Data%5Coutbox%5Cmessage.qmail" \
-d "attachments=E%3A%5Cfiles%5Cphoto.jpg,E%3A%5Cfiles%5Cdoc.pdf" \
-d "duration=8"
JavaScript (fetch)
const API_BASE = 'http://localhost:8080/api';
async function uploadEmail() {
const params = new URLSearchParams({
sender_id: '16777216',
sender_denomination: '1',
to: 'Sean#16777216,Bob#9876543',
email_file: 'E:\\Client_Data\\outbox\\message.qmail',
attachments: 'E:\\files\\photo.jpg,E:\\files\\doc.pdf',
cc: 'Alice#5555555',
duration: '4'
});
try {
const response = await fetch(`${API_BASE}/qmail/upload`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString()
});
const result = await response.json();
if (result.success) {
console.log('Upload completed!');
console.log('File GUID:', result.file_guid);
console.log('Files uploaded:', result.files_uploaded);
console.log('Total cost:', result.total_cost_cc, 'CC');
console.log(' Storage:', result.storage_cost_cc, 'CC');
console.log(' Inbox fees:', result.inbox_cost_cc, 'CC');
console.log('Upload successes:', result.upload_successes);
console.log('Tell success:', result.tell_success);
} else {
console.error('Upload failed:', result.message);
}
} catch (error) {
console.error('Error:', error);
}
}
uploadEmail();
Python
import requests
API_BASE = 'http://localhost:8080/api'
def upload_email():
params = {
'sender_id': '16777216',
'sender_denomination': '1',
'to': 'Sean#16777216,Bob#9876543',
'email_file': r'E:\Client_Data\outbox\message.qmail',
'attachments': r'E:\files\photo.jpg,E:\files\doc.pdf',
'cc': 'Alice#5555555',
'duration': 4
}
try:
response = requests.post(f'{API_BASE}/qmail/upload', data=params)
result = response.json()
if result.get('success'):
print('Upload completed!')
print(f'File GUID: {result["file_guid"]}')
print(f'Files uploaded: {result["files_uploaded"]}')
print(f'Total cost: {result["total_cost_cc"]} CC')
print(f' Storage: {result["storage_cost_cc"]} CC')
print(f' Inbox fees: {result["inbox_cost_cc"]} CC')
print(f'Upload successes: {result["upload_successes"]}')
print(f'Tell success: {result["tell_success"]}')
else:
print(f'Upload failed: {result["message"]}')
except Exception as e:
print(f'Error: {str(e)}')
upload_email()