/api/qmail/receipts
GETRead-only receipt fetcher. Returns the JSON contents of <Mail-wallet>/Receipts/<file_guid>.json as application/json. The receipt is the canonical record of an email's upload + Tell phases — created by /upload and updated through the lifecycle by /tell and the retry worker.
The link above uses 00000000000000000000000000000000 as a placeholder. Replace it with a real file_guid from an earlier /upload or /upload_and_tell response, otherwise the call returns HTTP 404.
Description
The /api/qmail/receipts endpoint streams the raw receipt JSON document for a single email. It is a thin read-only view — the server reads <Mail-wallet>/Receipts/<file_guid>.json and returns the bytes verbatim with Content-Type: application/json. There is no transformation, summarization, or filtering.
- Upload phase: per-file stripe table (server_id, locker_code, is_parity), per-server-success counts, total group size.
- Tell phase: per-recipient status (queued, sending, sent, failed, retry_queued), attempt counts, last_error, started/finished timestamps.
- Identity: sender SN/denom/email (the NON-secret subset). Bearer tokens (
sender_ans,enc_key.ans) are never serialized. - Pool reservations: inbox-fee locker key (string form), inbox_fee_acquired flag.
The retry worker (qmail_tell_retry.c) updates the receipt's matching recipient row on each retry via qmail_receipt_update_recipient(). Saves are atomic (tmp+rename), so a concurrent fetch always returns a consistent snapshot.
Per the security note in qmail_receipt.h, receipts hold only IDs, status, locker code strings, and metadata. sender_ans (the per-RAIDA AN bytes for the sender identity coin) and enc_key.ans (encryption-coin per-RAIDA AN bytes) are never serialized to disk — both are bearer tokens. /tell rehydrates them from the wallet at run time.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
guid |
string (32 hex chars) | Yes | The file_guid returned by a prior /upload or /upload_and_tell call. Returns HTTP 404 if no receipt exists. |
Response
On success, returns HTTP 200 OK with Content-Type: application/json and the raw receipt JSON bytes. The schema is documented in include/qmail/qmail_receipt.h (schema version 1). Below is an abbreviated example showing the most useful fields.
Success Response Example (Upload + Tell complete)
{
"version": 1,
"file_guid": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"request_id": "5a91c802",
"created_at": 1735689800,
"updated_at": 1735689812,
"wallet_path": "Default",
"sender": {
"serial_number": 55076,
"denomination": 1,
"email": "Sean.Worthington@Founder#FUV.giga"
},
"subject": "Upload+Tell Doc Test",
"body_preview": "QMail upload+tell doc test from Client1.",
"upload": {
"status": "success",
"started_at": 1735689800,
"finished_at": 1735689805,
"servers_total": 25,
"servers_ok": 25,
"servers_fail": 0,
"total_group_size": 1024,
"inbox_fee_locker": "FEE-001AABBCCDDEE",
"inbox_fee_acquired": true,
"files": [
{
"role": "email",
"file_type": 1,
"source": "body",
"size_bytes": 1024,
"status": "success",
"stripe_count": 25,
"stripes_uploaded": 25,
"stripes_failed": 0,
"stripes": [
{ "stripe_index": 0, "server_id": 0, "is_parity": false, "ok": true,
"stripe_size": 50, "locker_code": "STR-00-AABBCC11" },
{ "stripe_index": 1, "server_id": 1, "is_parity": false, "ok": true,
"stripe_size": 50, "locker_code": "STR-01-AABBCC22" }
]
}
]
},
"tell": {
"status": "success",
"summary_total": 1,
"summary_queued": 0,
"summary_sent": 1,
"summary_failed": 0,
"recipients": [
{
"address": "Mohsin.Mehraj@FullStack#FVH.giga",
"kind": "to",
"serial_number": 55077,
"denomination": 1,
"beacon_raida": 11,
"tell_command_code": 71,
"status": "sent",
"attempts": 1,
"last_error": "",
"started_at": 1735689810,
"finished_at": 1735689812
}
]
}
}
Success Response Example (Upload-only — no Tell yet)
{
"version": 1,
"file_guid": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"request_id": "5a91c802",
"created_at": 1735689800,
"updated_at": 1735689805,
"upload": {
"status": "success",
"servers_ok": 25,
"servers_fail": 0,
"inbox_fee_acquired": true
},
"tell": {
"status": "not_started",
"summary_total": 1,
"summary_queued": 1,
"summary_sent": 0,
"summary_failed": 0,
"recipients": [
{
"address": "Mohsin.Mehraj@FullStack#FVH.giga",
"kind": "to",
"serial_number": 55077,
"denomination": 1,
"beacon_raida": 11,
"tell_command_code": 71,
"status": "queued",
"attempts": 0
}
]
}
}
Success Response Example (Tell partial — retry queued)
{
"tell": {
"status": "partial",
"summary_total": 2,
"summary_sent": 1,
"summary_failed": 0,
"summary_queued": 1,
"recipients": [
{ "address": "...giga", "kind": "to", "status": "sent",
"attempts": 1 },
{ "address": "...giga", "kind": "cc", "status": "retry_queued",
"attempts": 2, "last_error": "Beacon RAIDA timeout" }
]
}
}
Error Response — Receipt Not Found (HTTP 404)
{
"error": true,
"message": "Receipt not found",
"code": 404
}
Error Response — Missing/Invalid GUID (HTTP 400)
{
"error": true,
"message": "Invalid guid format (expected 32 hex chars)",
"code": 400
}
Error Response — No Mail Wallet (HTTP 500)
{
"error": true,
"message": "No Mail wallet configured",
"code": 500
}
Interactive API Tester
Test this Endpoint
Examples
cURL
curl "http://localhost:8081/api/qmail/receipts?guid=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
JavaScript (fetch)
const fileGuid = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6';
const url = `http://localhost:8081/api/qmail/receipts?guid=${fileGuid}`;
const r = await fetch(url);
if (r.status === 404) {
console.warn('No receipt for that GUID yet — upload may still be running.');
} else if (r.ok) {
const receipt = await r.json();
console.log(`Upload status: ${receipt.upload?.status}`);
console.log(`Tell status: ${receipt.tell?.status}`);
receipt.tell?.recipients?.forEach(rc =>
console.log(` ${rc.kind} ${rc.address}: ${rc.status} (attempts=${rc.attempts})`)
);
}
Python
import requests
BASE = 'http://localhost:8081/api'
file_guid = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6'
r = requests.get(f'{BASE}/qmail/receipts', params={'guid': file_guid})
if r.status_code == 404:
print('No receipt for that GUID yet — upload may still be running.')
elif r.ok:
receipt = r.json()
print(f"Upload status: {receipt.get('upload', {}).get('status')}")
print(f"Tell status: {receipt.get('tell', {}).get('status')}")
for rc in receipt.get('tell', {}).get('recipients', []):
print(f" {rc['kind']} {rc['address']}: {rc['status']} (attempts={rc['attempts']})")
Important Notes
The receipt JSON includes "version": 1. Future schema changes will bump this and add a backwards-compatibility note in qmail_receipt.h.
This is a cheap read (one filesystem stat + one read of a few KB). Polling every 1–2 seconds during an active /upload_and_tell task is fine. For long-running uploads, prefer polling /api/system/tasks for the high-level status and only fetching the receipt when you need per-recipient detail.