/api/qmail/upload

GET

Upload 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.

What the GUI Must Provide

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 Address Format

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:

  1. Validate inputs — Check all required parameters, verify files exist and are readable, parse QMail addresses to binary.
  2. 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.
  3. Check balance — Verify the wallet has sufficient funds. Returns an error if the balance is too low.
  4. Generate File GUID — A single 16-byte GUID shared by all files in this upload.
  5. Acquire locker keys — Generate a unique 16-byte locker code for each of the 5 QMail servers.
  6. 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
  7. 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.
File Type Detection

The file type byte in the upload header identifies the content type of each file:

Type IDNameDescription
0QMAILPrimary email message file
1QTEXTReserved for future text processing
2QCHATReserved for future chat functionality
3PEER_TO_PEER_SECRET_CBDFPrivate user identification data
4GROUPS_SECRET_CBDFPrivate group identification data
5QPACKETReserved for future packet management
6QDATAFile management for QData servers
10-255Attachment NFile attachments (10 = first attachment, etc.)

Response

Returns a JSON object indicating the result of the upload operation.

Success Response Properties

status string
"success" when the upload completes successfully.
operation string
Always "upload".
message string
Human-readable status message.
file_guid string
32-character hexadecimal File GUID shared by all uploaded files.
files_uploaded integer
Total number of files successfully uploaded (email + attachments).
total_cost_cc integer
Total cost in CloudCoin (storage + inbox fees).
storage_cost_cc integer
Storage cost component in CloudCoin.
inbox_cost_cc integer
Inbox fee component in CloudCoin.
upload_successes integer
Total number of successful stripe uploads across all files.
upload_failures integer
Total number of failed stripe uploads across all files.
tell_success boolean
Whether recipients were successfully notified via TELL2.
duration_weeks integer
Storage duration that was applied (in weeks).

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

status string
"fail" when the upload fails.
operation string
Always "upload".
error_code integer
Machine-readable error code identifying the failure type.
message string
Human-readable error description.

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

Serial number of your identity coin
Human-readable value (e.g., 1, 0.01)
Comma-separated QMail addresses (Name#SerialNumber)
Absolute path to the binary .qmail email file
Comma-separated absolute file paths

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()