/api/qmail/net/messages/upload

GET

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

Wallet Selection

If you omit wallet and wallet_path, resolve_wallet_path() defaults to the Default wallet. That is the recommended doc/test setup for Client1.

Recipient Format

Use repeated to=, cc=, and bcc= parameters for multiple recipients. Legacy comma/semicolon-separated strings are still accepted for backward compatibility.

Recipient Limits

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:

  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 immediately indicating that the asynchronous upload task has started. Poll /api/system/tasks with the returned task_id for completion details.

Success Response Properties

command string
Always "qmail-upload".
success boolean
true when the task was created successfully.
message string
Human-readable status message.
task_id string
Task identifier for polling /api/system/tasks.
url string
Fully-formed poll URL the caller can GET to fetch this task's status. Equivalent to /api/system/tasks?task_id={task_id} on the same host. Provided so clients do not need to build the URL themselves.
source / email_file / attachments / to / duration / tell_enabled mixed
Echo fields describing which mode was started and what arguments were accepted.

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

error boolean
true on synchronous validation/startup failures.
message string
Human-readable error description.
code integer
HTTP status code repeated in the JSON body.

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

Enter one recipient per line. The tester sends them as repeated to= parameters.
Leave this blank to test body mode instead of file mode
Comma-separated absolute file paths

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