Jelajahi Sumber

docs: expand API and mailbox guides

AI-Co-Authored-By: Codex
chendeben 1 bulan lalu
induk
melakukan
c89348ddcd

+ 128 - 18
landing.html

@@ -18,6 +18,7 @@
           <a href="#quickstart" data-i18n="nav.quickstart">Quick start</a>
           <a href="#api" data-i18n="nav.api">API</a>
           <a href="#mailboxes" data-i18n="nav.mailboxes">Mailboxes</a>
+          <a href="#receiving" data-i18n="nav.receiving">Receiving</a>
           <a href="#domains" data-i18n="nav.domains">Domain setup</a>
           <a href="#smtp" data-i18n="nav.smtp">SMTP</a>
           <a href="#webhooks" data-i18n="nav.webhooks">Webhooks</a>
@@ -121,8 +122,24 @@
 
         <section id="api" class="section">
           <div class="section-head">
-            <h2 data-i18n="api.title">Send API</h2>
-            <p data-i18n="api.sub">Minimal JSON contract. Hostnames below are placeholders — replace with your MailHub host.</p>
+            <h2 data-i18n="api.title">API authentication and sending</h2>
+            <p data-i18n="api.sub">All public API calls use a Bearer token. The hostname below is a placeholder; replace it with your MailHub host.</p>
+          </div>
+          <div class="doc-grid">
+            <article class="card">
+              <h3 data-i18n="api.auth.title">Create and protect a token</h3>
+              <p data-i18n="api.auth.body">Create a token in Console &gt; API Tokens. The complete secret is shown only once, so store it outside browser code and public repositories.</p>
+              <ul class="field-list compact-list">
+                <li><code>send</code> — <span data-i18n="api.auth.send">send messages with POST /api/send</span></li>
+                <li><code>mailboxes:read</code> — <span data-i18n="api.auth.read">list mailboxes with GET /api/mailboxes</span></li>
+                <li><code>mailboxes:write</code> — <span data-i18n="api.auth.write">create mailboxes with POST /api/mailboxes</span></li>
+              </ul>
+            </article>
+            <article class="card">
+              <h3 data-i18n="api.send.title">Send a message</h3>
+              <p data-i18n="api.send.body">The From domain must belong to the token account. When verified sending is enabled, complete the domain DNS checks before sending.</p>
+              <p class="note compact-note"><code>Authorization: Bearer &lt;USER_API_TOKEN&gt;</code></p>
+            </article>
           </div>
           <div class="code-card">
             <div class="code-card-bar">
@@ -131,12 +148,31 @@
             </div>
             <pre id="api-curl"><code data-i18n-html="api.curl"></code></pre>
           </div>
-          <ul class="field-list">
-            <li><code>from</code> — <span data-i18n="api.field.from">sender address on a verified domain</span></li>
-            <li><code>to</code> — <span data-i18n="api.field.to">recipient string or array</span></li>
-            <li><code>subject</code> — <span data-i18n="api.field.subject">message subject</span></li>
-            <li><code>text</code> / <code>html</code> — <span data-i18n="api.field.body">message body</span></li>
-          </ul>
+          <div class="table-wrap">
+            <table>
+              <thead>
+                <tr>
+                  <th data-i18n="api.fields.name">Field</th>
+                  <th data-i18n="api.fields.required">Required</th>
+                  <th data-i18n="api.fields.description">Description</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr><td><code>from</code></td><td data-i18n="common.yes">Yes</td><td data-i18n="api.field.from">sender address on an account-owned domain</td></tr>
+                <tr><td><code>to</code></td><td data-i18n="common.yes">Yes</td><td data-i18n="api.field.to">recipient string or an address array</td></tr>
+                <tr><td><code>subject</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.subject">defaults to (no subject)</td></tr>
+                <tr><td><code>text</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.text">plain-text body</td></tr>
+                <tr><td><code>html</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.html">HTML body; required when enabling open or click tracking</td></tr>
+                <tr><td><code>tracking</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.tracking">true/false, or { opens, clicks }; rewrites HTML only</td></tr>
+                <tr><td><code>smtpRelayId</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.relay">an account-owned outbound SMTP relay ID</td></tr>
+              </tbody>
+            </table>
+          </div>
+          <div class="code-card doc-code-card">
+            <div class="code-card-bar"><span data-i18n="api.responseLabel">202 response</span></div>
+            <pre><code data-i18n-html="api.response"></code></pre>
+          </div>
+          <p class="note" data-i18n="api.note">A 202 response means MailHub accepted the message for SMTP delivery. Use the console delivery log or webhooks to observe the later result.</p>
         </section>
 
         <section id="mailboxes" class="section section-alt">
@@ -146,11 +182,11 @@
           </div>
           <div class="doc-grid">
             <article class="card">
-              <h3 data-i18n="mailboxes.api.title">Create a mailbox</h3>
-              <p data-i18n="mailboxes.api.body">POST to /api/mailboxes with a token holding mailboxes:write. The target domain must belong to that account.</p>
+              <h3 data-i18n="mailboxes.api.title">Mailbox API scope</h3>
+              <p data-i18n="mailboxes.api.body">POST /api/mailboxes requires mailboxes:write. The target domain must belong to the token account.</p>
               <ul class="field-list compact-list">
                 <li><code>mode</code> — <span data-i18n="mailboxes.api.mode">permanent or temporary</span></li>
-                <li><code>address</code> / <code>domain</code> — <span data-i18n="mailboxes.api.address">use a full address, or let a temporary mailbox generate one from domain</span></li>
+                <li><code>address</code> / <code>domain</code> — <span data-i18n="mailboxes.api.address">use a full address, or choose domain plus localPart</span></li>
                 <li><code>expiresInMinutes</code> — <span data-i18n="mailboxes.api.expires">required for temporary mailboxes; 5 minutes to 30 days</span></li>
                 <li><code>password</code> — <span data-i18n="mailboxes.api.password">optional; a secure password is returned once when omitted</span></li>
               </ul>
@@ -165,14 +201,64 @@
               </ul>
             </article>
           </div>
-          <div class="code-card doc-code-card">
-            <div class="code-card-bar">
-              <span>POST /api/mailboxes</span>
-              <button type="button" class="copy-btn" data-copy-target="mailbox-curl" data-i18n="common.copy">Copy</button>
+          <div class="reference-grid">
+            <div class="code-card doc-code-card">
+              <div class="code-card-bar"><span data-i18n="mailboxes.permanent.label">POST /api/mailboxes - permanent</span><button type="button" class="copy-btn" data-copy-target="mailbox-permanent-curl" data-i18n="common.copy">Copy</button></div>
+              <pre id="mailbox-permanent-curl"><code data-i18n-html="mailboxes.permanent.curl"></code></pre>
             </div>
-            <pre id="mailbox-curl"><code data-i18n-html="mailboxes.curl"></code></pre>
+            <div class="code-card doc-code-card">
+              <div class="code-card-bar"><span data-i18n="mailboxes.temporary.label">POST /api/mailboxes - temporary</span><button type="button" class="copy-btn" data-copy-target="mailbox-temporary-curl" data-i18n="common.copy">Copy</button></div>
+              <pre id="mailbox-temporary-curl"><code data-i18n-html="mailboxes.temporary.curl"></code></pre>
+            </div>
+          </div>
+          <div class="code-card doc-code-card">
+            <div class="code-card-bar"><span>GET /api/mailboxes</span><button type="button" class="copy-btn" data-copy-target="mailbox-list-curl" data-i18n="common.copy">Copy</button></div>
+            <pre id="mailbox-list-curl"><code data-i18n-html="mailboxes.list.curl"></code></pre>
+          </div>
+          <div class="code-card doc-code-card">
+            <div class="code-card-bar"><span data-i18n="mailboxes.responseLabel">201 creation response</span></div>
+            <pre><code data-i18n-html="mailboxes.response"></code></pre>
           </div>
-          <p class="note" data-i18n="mailboxes.note">Use GET /api/mailboxes with mailboxes:read to list the account's mailboxes. A temporary mailbox stops accepting mail and authenticating at its expiration time.</p>
+          <ul class="field-list">
+            <li data-i18n="mailboxes.lifecycle.password">The returned password is the only opportunity to read an automatically generated password.</li>
+            <li data-i18n="mailboxes.lifecycle.temporary">A temporary mailbox expires after the requested interval and then stops receiving mail and authenticating.</li>
+            <li data-i18n="mailboxes.lifecycle.list">Use mailboxes:read to list mailbox metadata; passwords are never returned by the list endpoint.</li>
+          </ul>
+        </section>
+
+        <section id="receiving" class="section">
+          <div class="section-head">
+            <h2 data-i18n="receiving.title">Receiving mail</h2>
+            <p data-i18n="receiving.sub">Mail arrives through the domain MX record. Read it in MailHub Inbox or through standard IMAP/POP3 clients; there is no separate inbound Bearer API.</p>
+          </div>
+          <div class="doc-grid">
+            <article class="card">
+              <h3 data-i18n="receiving.delivery.title">Inbound delivery</h3>
+              <p data-i18n="receiving.delivery.body">Set the domain MX record to the receiving host. External mail servers then deliver to SMTP port 25, and MailHub stores mail for an existing mailbox, alias, or catch-all target.</p>
+            </article>
+            <article class="card">
+              <h3 data-i18n="receiving.routing.title">Mailbox, aliases, and catch-all</h3>
+              <p data-i18n="receiving.routing.body">Create a full mailbox address first. Add aliases or forwarding on that mailbox, and configure the domain catch-all in the console to route unknown local parts to a mailbox or another address.</p>
+            </article>
+          </div>
+          <div class="table-wrap">
+            <table>
+              <thead>
+                <tr>
+                  <th data-i18n="receiving.protocols.protocol">Protocol</th>
+                  <th data-i18n="receiving.protocols.ports">Recommended ports</th>
+                  <th data-i18n="receiving.protocols.security">Security</th>
+                  <th data-i18n="receiving.protocols.use">Use</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr><td>IMAP</td><td><code>993</code> / <code>143</code></td><td data-i18n="receiving.protocols.imapSecurity">SSL/TLS or STARTTLS</td><td data-i18n="receiving.protocols.imapUse">recommended for synchronized folders</td></tr>
+                <tr><td>POP3</td><td><code>995</code> / <code>110</code></td><td data-i18n="receiving.protocols.pop3Security">SSL/TLS or STLS</td><td data-i18n="receiving.protocols.pop3Use">download-oriented clients</td></tr>
+                <tr><td>SMTP</td><td><code>465</code> / <code>587</code></td><td data-i18n="receiving.protocols.smtpSecurity">SMTPS or STARTTLS</td><td data-i18n="receiving.protocols.smtpUse">send with the same mailbox address and password</td></tr>
+              </tbody>
+            </table>
+          </div>
+          <p class="note" data-i18n="receiving.folders">IMAP exposes INBOX, Sent, Drafts, Trash, Junk, and Archive. Use IMAP when the client should synchronize these folders and save sent messages.</p>
         </section>
 
         <section id="domains" class="section">
@@ -212,7 +298,31 @@
             <div class="card mono"><strong>465</strong><span>smtps</span></div>
             <div class="card mono"><strong>2525</strong><span>smtp</span></div>
           </div>
-          <p class="note" data-i18n="smtp.note">Use a mailbox's full address and password for SMTP, IMAP, and POP3. Prefer TLS ports 465, 993, and 995; use STARTTLS/STLS on the plain ports when needed.</p>
+          <div class="doc-grid">
+            <article class="card"><h3 data-i18n="smtp.app.title">Application SMTP</h3><p data-i18n="smtp.app.body">Create an SMTP login credential in the console for an application. Use that credential with an account-owned From domain.</p></article>
+            <article class="card"><h3 data-i18n="smtp.mailbox.title">Mailbox SMTP</h3><p data-i18n="smtp.mailbox.body">A created mailbox can also authenticate over SMTP with its full email address and password. Use the same credentials for IMAP or POP3.</p></article>
+          </div>
+          <p class="note" data-i18n="smtp.note">Prefer 465 or 587 for sending, 993 for IMAP, and 995 for POP3. The creation response is the source of truth when a deployment overrides default ports or TLS listeners.</p>
+        </section>
+
+        <section id="errors" class="section">
+          <div class="section-head">
+            <h2 data-i18n="errors.title">Responses and common errors</h2>
+            <p data-i18n="errors.sub">The API returns JSON. Validate the HTTP status before consuming a response body.</p>
+          </div>
+          <div class="table-wrap">
+            <table>
+              <thead><tr><th data-i18n="errors.status">Status</th><th data-i18n="errors.meaning">Meaning</th><th data-i18n="errors.action">What to do</th></tr></thead>
+              <tbody>
+                <tr><td><code>201</code></td><td data-i18n="errors.201.meaning">Mailbox created</td><td data-i18n="errors.201.action">Store the one-time password and clientConfig.</td></tr>
+                <tr><td><code>202</code></td><td data-i18n="errors.202.meaning">Message accepted for SMTP delivery</td><td data-i18n="errors.202.action">Inspect delivery logs or webhooks for later delivery status.</td></tr>
+                <tr><td><code>400</code></td><td data-i18n="errors.400.meaning">Invalid request or domain ownership rule</td><td data-i18n="errors.400.action">Read the JSON error and correct the request body or domain setup.</td></tr>
+                <tr><td><code>401</code></td><td data-i18n="errors.401.meaning">Missing, invalid, revoked, or expired token</td><td data-i18n="errors.401.action">Create or rotate a valid token.</td></tr>
+                <tr><td><code>403</code></td><td data-i18n="errors.403.meaning">Token does not have the required scope</td><td data-i18n="errors.403.action">Grant the minimum required scope in Console &gt; API Tokens.</td></tr>
+                <tr><td><code>409</code></td><td data-i18n="errors.409.meaning">Mailbox address already exists</td><td data-i18n="errors.409.action">Choose another address or use the existing mailbox.</td></tr>
+              </tbody>
+            </table>
+          </div>
         </section>
 
         <section id="webhooks" class="section">

File diff ditekan karena terlalu besar
+ 8 - 0
public/assets/landing-BeR3r5vM.js


File diff ditekan karena terlalu besar
+ 0 - 0
public/assets/landing-BkPrzdt8.css


File diff ditekan karena terlalu besar
+ 0 - 8
public/assets/landing-OL30yro5.js


+ 130 - 20
public/landing.html

@@ -5,9 +5,9 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <meta name="description" content="MailHub — self-hosted transactional email with API, SMTP, DNS and webhooks." />
     <title>MailHub</title>
-    <script type="module" crossorigin src="/assets/landing-OL30yro5.js"></script>
+    <script type="module" crossorigin src="/assets/landing-BeR3r5vM.js"></script>
     <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-Dezn_h7o.js">
-    <link rel="stylesheet" crossorigin href="/assets/landing-C6XYaH1w.css">
+    <link rel="stylesheet" crossorigin href="/assets/landing-BkPrzdt8.css">
   </head>
   <body>
     <div class="page">
@@ -21,6 +21,7 @@
           <a href="#quickstart" data-i18n="nav.quickstart">Quick start</a>
           <a href="#api" data-i18n="nav.api">API</a>
           <a href="#mailboxes" data-i18n="nav.mailboxes">Mailboxes</a>
+          <a href="#receiving" data-i18n="nav.receiving">Receiving</a>
           <a href="#domains" data-i18n="nav.domains">Domain setup</a>
           <a href="#smtp" data-i18n="nav.smtp">SMTP</a>
           <a href="#webhooks" data-i18n="nav.webhooks">Webhooks</a>
@@ -124,8 +125,24 @@
 
         <section id="api" class="section">
           <div class="section-head">
-            <h2 data-i18n="api.title">Send API</h2>
-            <p data-i18n="api.sub">Minimal JSON contract. Hostnames below are placeholders — replace with your MailHub host.</p>
+            <h2 data-i18n="api.title">API authentication and sending</h2>
+            <p data-i18n="api.sub">All public API calls use a Bearer token. The hostname below is a placeholder; replace it with your MailHub host.</p>
+          </div>
+          <div class="doc-grid">
+            <article class="card">
+              <h3 data-i18n="api.auth.title">Create and protect a token</h3>
+              <p data-i18n="api.auth.body">Create a token in Console &gt; API Tokens. The complete secret is shown only once, so store it outside browser code and public repositories.</p>
+              <ul class="field-list compact-list">
+                <li><code>send</code> — <span data-i18n="api.auth.send">send messages with POST /api/send</span></li>
+                <li><code>mailboxes:read</code> — <span data-i18n="api.auth.read">list mailboxes with GET /api/mailboxes</span></li>
+                <li><code>mailboxes:write</code> — <span data-i18n="api.auth.write">create mailboxes with POST /api/mailboxes</span></li>
+              </ul>
+            </article>
+            <article class="card">
+              <h3 data-i18n="api.send.title">Send a message</h3>
+              <p data-i18n="api.send.body">The From domain must belong to the token account. When verified sending is enabled, complete the domain DNS checks before sending.</p>
+              <p class="note compact-note"><code>Authorization: Bearer &lt;USER_API_TOKEN&gt;</code></p>
+            </article>
           </div>
           <div class="code-card">
             <div class="code-card-bar">
@@ -134,12 +151,31 @@
             </div>
             <pre id="api-curl"><code data-i18n-html="api.curl"></code></pre>
           </div>
-          <ul class="field-list">
-            <li><code>from</code> — <span data-i18n="api.field.from">sender address on a verified domain</span></li>
-            <li><code>to</code> — <span data-i18n="api.field.to">recipient string or array</span></li>
-            <li><code>subject</code> — <span data-i18n="api.field.subject">message subject</span></li>
-            <li><code>text</code> / <code>html</code> — <span data-i18n="api.field.body">message body</span></li>
-          </ul>
+          <div class="table-wrap">
+            <table>
+              <thead>
+                <tr>
+                  <th data-i18n="api.fields.name">Field</th>
+                  <th data-i18n="api.fields.required">Required</th>
+                  <th data-i18n="api.fields.description">Description</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr><td><code>from</code></td><td data-i18n="common.yes">Yes</td><td data-i18n="api.field.from">sender address on an account-owned domain</td></tr>
+                <tr><td><code>to</code></td><td data-i18n="common.yes">Yes</td><td data-i18n="api.field.to">recipient string or an address array</td></tr>
+                <tr><td><code>subject</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.subject">defaults to (no subject)</td></tr>
+                <tr><td><code>text</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.text">plain-text body</td></tr>
+                <tr><td><code>html</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.html">HTML body; required when enabling open or click tracking</td></tr>
+                <tr><td><code>tracking</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.tracking">true/false, or { opens, clicks }; rewrites HTML only</td></tr>
+                <tr><td><code>smtpRelayId</code></td><td data-i18n="common.no">No</td><td data-i18n="api.field.relay">an account-owned outbound SMTP relay ID</td></tr>
+              </tbody>
+            </table>
+          </div>
+          <div class="code-card doc-code-card">
+            <div class="code-card-bar"><span data-i18n="api.responseLabel">202 response</span></div>
+            <pre><code data-i18n-html="api.response"></code></pre>
+          </div>
+          <p class="note" data-i18n="api.note">A 202 response means MailHub accepted the message for SMTP delivery. Use the console delivery log or webhooks to observe the later result.</p>
         </section>
 
         <section id="mailboxes" class="section section-alt">
@@ -149,11 +185,11 @@
           </div>
           <div class="doc-grid">
             <article class="card">
-              <h3 data-i18n="mailboxes.api.title">Create a mailbox</h3>
-              <p data-i18n="mailboxes.api.body">POST to /api/mailboxes with a token holding mailboxes:write. The target domain must belong to that account.</p>
+              <h3 data-i18n="mailboxes.api.title">Mailbox API scope</h3>
+              <p data-i18n="mailboxes.api.body">POST /api/mailboxes requires mailboxes:write. The target domain must belong to the token account.</p>
               <ul class="field-list compact-list">
                 <li><code>mode</code> — <span data-i18n="mailboxes.api.mode">permanent or temporary</span></li>
-                <li><code>address</code> / <code>domain</code> — <span data-i18n="mailboxes.api.address">use a full address, or let a temporary mailbox generate one from domain</span></li>
+                <li><code>address</code> / <code>domain</code> — <span data-i18n="mailboxes.api.address">use a full address, or choose domain plus localPart</span></li>
                 <li><code>expiresInMinutes</code> — <span data-i18n="mailboxes.api.expires">required for temporary mailboxes; 5 minutes to 30 days</span></li>
                 <li><code>password</code> — <span data-i18n="mailboxes.api.password">optional; a secure password is returned once when omitted</span></li>
               </ul>
@@ -168,14 +204,64 @@
               </ul>
             </article>
           </div>
-          <div class="code-card doc-code-card">
-            <div class="code-card-bar">
-              <span>POST /api/mailboxes</span>
-              <button type="button" class="copy-btn" data-copy-target="mailbox-curl" data-i18n="common.copy">Copy</button>
+          <div class="reference-grid">
+            <div class="code-card doc-code-card">
+              <div class="code-card-bar"><span data-i18n="mailboxes.permanent.label">POST /api/mailboxes - permanent</span><button type="button" class="copy-btn" data-copy-target="mailbox-permanent-curl" data-i18n="common.copy">Copy</button></div>
+              <pre id="mailbox-permanent-curl"><code data-i18n-html="mailboxes.permanent.curl"></code></pre>
             </div>
-            <pre id="mailbox-curl"><code data-i18n-html="mailboxes.curl"></code></pre>
+            <div class="code-card doc-code-card">
+              <div class="code-card-bar"><span data-i18n="mailboxes.temporary.label">POST /api/mailboxes - temporary</span><button type="button" class="copy-btn" data-copy-target="mailbox-temporary-curl" data-i18n="common.copy">Copy</button></div>
+              <pre id="mailbox-temporary-curl"><code data-i18n-html="mailboxes.temporary.curl"></code></pre>
+            </div>
+          </div>
+          <div class="code-card doc-code-card">
+            <div class="code-card-bar"><span>GET /api/mailboxes</span><button type="button" class="copy-btn" data-copy-target="mailbox-list-curl" data-i18n="common.copy">Copy</button></div>
+            <pre id="mailbox-list-curl"><code data-i18n-html="mailboxes.list.curl"></code></pre>
+          </div>
+          <div class="code-card doc-code-card">
+            <div class="code-card-bar"><span data-i18n="mailboxes.responseLabel">201 creation response</span></div>
+            <pre><code data-i18n-html="mailboxes.response"></code></pre>
           </div>
-          <p class="note" data-i18n="mailboxes.note">Use GET /api/mailboxes with mailboxes:read to list the account's mailboxes. A temporary mailbox stops accepting mail and authenticating at its expiration time.</p>
+          <ul class="field-list">
+            <li data-i18n="mailboxes.lifecycle.password">The returned password is the only opportunity to read an automatically generated password.</li>
+            <li data-i18n="mailboxes.lifecycle.temporary">A temporary mailbox expires after the requested interval and then stops receiving mail and authenticating.</li>
+            <li data-i18n="mailboxes.lifecycle.list">Use mailboxes:read to list mailbox metadata; passwords are never returned by the list endpoint.</li>
+          </ul>
+        </section>
+
+        <section id="receiving" class="section">
+          <div class="section-head">
+            <h2 data-i18n="receiving.title">Receiving mail</h2>
+            <p data-i18n="receiving.sub">Mail arrives through the domain MX record. Read it in MailHub Inbox or through standard IMAP/POP3 clients; there is no separate inbound Bearer API.</p>
+          </div>
+          <div class="doc-grid">
+            <article class="card">
+              <h3 data-i18n="receiving.delivery.title">Inbound delivery</h3>
+              <p data-i18n="receiving.delivery.body">Set the domain MX record to the receiving host. External mail servers then deliver to SMTP port 25, and MailHub stores mail for an existing mailbox, alias, or catch-all target.</p>
+            </article>
+            <article class="card">
+              <h3 data-i18n="receiving.routing.title">Mailbox, aliases, and catch-all</h3>
+              <p data-i18n="receiving.routing.body">Create a full mailbox address first. Add aliases or forwarding on that mailbox, and configure the domain catch-all in the console to route unknown local parts to a mailbox or another address.</p>
+            </article>
+          </div>
+          <div class="table-wrap">
+            <table>
+              <thead>
+                <tr>
+                  <th data-i18n="receiving.protocols.protocol">Protocol</th>
+                  <th data-i18n="receiving.protocols.ports">Recommended ports</th>
+                  <th data-i18n="receiving.protocols.security">Security</th>
+                  <th data-i18n="receiving.protocols.use">Use</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr><td>IMAP</td><td><code>993</code> / <code>143</code></td><td data-i18n="receiving.protocols.imapSecurity">SSL/TLS or STARTTLS</td><td data-i18n="receiving.protocols.imapUse">recommended for synchronized folders</td></tr>
+                <tr><td>POP3</td><td><code>995</code> / <code>110</code></td><td data-i18n="receiving.protocols.pop3Security">SSL/TLS or STLS</td><td data-i18n="receiving.protocols.pop3Use">download-oriented clients</td></tr>
+                <tr><td>SMTP</td><td><code>465</code> / <code>587</code></td><td data-i18n="receiving.protocols.smtpSecurity">SMTPS or STARTTLS</td><td data-i18n="receiving.protocols.smtpUse">send with the same mailbox address and password</td></tr>
+              </tbody>
+            </table>
+          </div>
+          <p class="note" data-i18n="receiving.folders">IMAP exposes INBOX, Sent, Drafts, Trash, Junk, and Archive. Use IMAP when the client should synchronize these folders and save sent messages.</p>
         </section>
 
         <section id="domains" class="section">
@@ -215,7 +301,31 @@
             <div class="card mono"><strong>465</strong><span>smtps</span></div>
             <div class="card mono"><strong>2525</strong><span>smtp</span></div>
           </div>
-          <p class="note" data-i18n="smtp.note">Use a mailbox's full address and password for SMTP, IMAP, and POP3. Prefer TLS ports 465, 993, and 995; use STARTTLS/STLS on the plain ports when needed.</p>
+          <div class="doc-grid">
+            <article class="card"><h3 data-i18n="smtp.app.title">Application SMTP</h3><p data-i18n="smtp.app.body">Create an SMTP login credential in the console for an application. Use that credential with an account-owned From domain.</p></article>
+            <article class="card"><h3 data-i18n="smtp.mailbox.title">Mailbox SMTP</h3><p data-i18n="smtp.mailbox.body">A created mailbox can also authenticate over SMTP with its full email address and password. Use the same credentials for IMAP or POP3.</p></article>
+          </div>
+          <p class="note" data-i18n="smtp.note">Prefer 465 or 587 for sending, 993 for IMAP, and 995 for POP3. The creation response is the source of truth when a deployment overrides default ports or TLS listeners.</p>
+        </section>
+
+        <section id="errors" class="section">
+          <div class="section-head">
+            <h2 data-i18n="errors.title">Responses and common errors</h2>
+            <p data-i18n="errors.sub">The API returns JSON. Validate the HTTP status before consuming a response body.</p>
+          </div>
+          <div class="table-wrap">
+            <table>
+              <thead><tr><th data-i18n="errors.status">Status</th><th data-i18n="errors.meaning">Meaning</th><th data-i18n="errors.action">What to do</th></tr></thead>
+              <tbody>
+                <tr><td><code>201</code></td><td data-i18n="errors.201.meaning">Mailbox created</td><td data-i18n="errors.201.action">Store the one-time password and clientConfig.</td></tr>
+                <tr><td><code>202</code></td><td data-i18n="errors.202.meaning">Message accepted for SMTP delivery</td><td data-i18n="errors.202.action">Inspect delivery logs or webhooks for later delivery status.</td></tr>
+                <tr><td><code>400</code></td><td data-i18n="errors.400.meaning">Invalid request or domain ownership rule</td><td data-i18n="errors.400.action">Read the JSON error and correct the request body or domain setup.</td></tr>
+                <tr><td><code>401</code></td><td data-i18n="errors.401.meaning">Missing, invalid, revoked, or expired token</td><td data-i18n="errors.401.action">Create or rotate a valid token.</td></tr>
+                <tr><td><code>403</code></td><td data-i18n="errors.403.meaning">Token does not have the required scope</td><td data-i18n="errors.403.action">Grant the minimum required scope in Console &gt; API Tokens.</td></tr>
+                <tr><td><code>409</code></td><td data-i18n="errors.409.meaning">Mailbox address already exists</td><td data-i18n="errors.409.action">Choose another address or use the existing mailbox.</td></tr>
+              </tbody>
+            </table>
+          </div>
         </section>
 
         <section id="webhooks" class="section">

+ 218 - 36
src/frontend/landing/i18n.js

@@ -8,6 +8,7 @@ const messages = {
     'nav.quickstart': '使用说明',
     'nav.api': 'API',
     'nav.mailboxes': '邮箱',
+    'nav.receiving': '收信',
     'nav.domains': '域名配置',
     'nav.smtp': 'SMTP',
     'nav.webhooks': 'Webhooks',
@@ -17,6 +18,8 @@ const messages = {
     'cta.github': 'GitHub',
     'common.copy': '复制',
     'common.copied': '已复制',
+    'common.yes': '是',
+    'common.no': '否',
     'hero.eyebrow': '事务邮件平台',
     'hero.title': '用 API 与 SMTP 发送可靠的事务邮件',
     'hero.lede': '自托管 MailHub 提供域名 DNS 引导、DKIM 签名、投递日志与签名 Webhook —— 无需被黑盒 SaaS 锁定。',
@@ -72,8 +75,15 @@ const messages = {
     'quickstart.client.title': '接入客户端',
     'quickstart.client.body': '使用 SMTP 发信,用 IMAP 或 POP3 收信,均使用完整邮箱地址和密码。',
     'quickstart.client.link': '查看协议配置 →',
-    'api.title': '发送 API',
-    'api.sub': '最小 JSON 契约。下列主机名为占位符,请替换为你的 MailHub 地址。',
+    'api.title': 'API 认证与发信',
+    'api.sub': '所有公开 API 均使用 Bearer Token。下列主机名为占位符,请替换为你的 MailHub 地址。',
+    'api.auth.title': '创建并保护 Token',
+    'api.auth.body': '在控制台的 API Token 页面创建 Token。完整密钥仅显示一次,请存储在服务端环境,切勿写入浏览器代码或公开仓库。',
+    'api.auth.send': '使用 POST /api/send 发信',
+    'api.auth.read': '使用 GET /api/mailboxes 查询邮箱',
+    'api.auth.write': '使用 POST /api/mailboxes 创建邮箱',
+    'api.send.title': '发送一封邮件',
+    'api.send.body': 'From 域名必须属于该 Token 所在账号。若启用了已验证域名发信,请先完成控制台中的 DNS 校验。',
     'api.curl': `curl -X POST https://mail.example.com/api/send \\
   -H 'Authorization: Bearer &lt;USER_API_TOKEN&gt;' \\
   -H 'Content-Type: application/json' \\
@@ -83,16 +93,33 @@ const messages = {
     "subject": "Hello from MailHub",
     "text": "Signed with DKIM and queued by MailHub."
   }'`,
-    'api.field.from': '已验证域名上的发件地址',
-    'api.field.to': '收件人字符串或数组',
-    'api.field.subject': '邮件主题',
-    'api.field.body': '纯文本或 HTML 正文',
+    'api.fields.name': '字段',
+    'api.fields.required': '必填',
+    'api.fields.description': '说明',
+    'api.field.from': '该账号已添加域名上的发件地址',
+    'api.field.to': '单个收件人字符串或地址数组',
+    'api.field.subject': '默认值为 (no subject)',
+    'api.field.text': '纯文本正文',
+    'api.field.html': 'HTML 正文;启用打开或点击跟踪时需要提供',
+    'api.field.tracking': 'true/false,或 { opens, clicks };仅改写 HTML',
+    'api.field.relay': '属于当前账号的外部 SMTP 出口 ID',
+    'api.responseLabel': '202 响应',
+    'api.response': `{
+  "eventId": 42,
+  "queued": true,
+  "domain": "example.com",
+  "recipients": ["user@example.com"],
+  "smtp": "Message queued",
+  "queueId": "AB12CD34",
+  "tracking": { "enabled": false, "opens": false, "clicks": false }
+}`,
+    'api.note': '202 表示 MailHub 已接受邮件并交给 SMTP 投递。后续投递结果请查看控制台日志或使用 Webhook。',
     'mailboxes.title': '邮箱 API 与客户端接入',
     'mailboxes.sub': '为持续使用创建长期邮箱;为短流程创建可自动过期的临时邮箱。',
-    'mailboxes.api.title': '创建邮箱',
+    'mailboxes.api.title': '邮箱 API 权限',
     'mailboxes.api.body': '使用拥有 mailboxes:write 权限的 Token 调用 POST /api/mailboxes,目标域名必须属于该账号。',
     'mailboxes.api.mode': 'permanent 或 temporary',
-    'mailboxes.api.address': '传入完整邮箱,或只传 domain 让临时邮箱自动生成地址',
+    'mailboxes.api.address': '传入完整邮箱,或使用 domain 加 localPart',
     'mailboxes.api.expires': '临时邮箱必填,范围为 5 分钟到 30 天',
     'mailboxes.api.password': '可选;不传时会仅在创建响应中返回安全密码',
     'mailboxes.client.title': '使用返回的客户端配置',
@@ -100,15 +127,58 @@ const messages = {
     'mailboxes.client.imap': '推荐,支持同步文件夹与已发送邮件',
     'mailboxes.client.pop3': '适合下载到本地的客户端',
     'mailboxes.client.smtp': '发信时同样使用完整邮箱地址和密码',
-    'mailboxes.curl': `curl -X POST https://mail.example.com/api/mailboxes \\
-  -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' \\
-  -H 'Content-Type: application/json' \\
-  -d '{
-    "mode": "temporary",
-    "domain": "example.com",
-    "expiresInMinutes": 60
-  }'`,
-    'mailboxes.note': '使用带有 mailboxes:read 权限的 GET /api/mailboxes 查询该账号的邮箱。临时邮箱到期后会停止收信与认证。',
+    'mailboxes.permanent.label': 'POST /api/mailboxes - 长期邮箱',
+    'mailboxes.permanent.curl': `curl -X POST https://mail.example.com/api/mailboxes -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' -H 'Content-Type: application/json' -d '{
+  "mode": "permanent",
+  "address": "support@example.com",
+  "password": "&lt;MAILBOX_PASSWORD&gt;",
+  "displayName": "Support",
+  "aliases": ["help"],
+  "forwardTo": ["archive@example.net"],
+  "keepForwarded": true,
+  "quotaMb": 1024
+}'`,
+    'mailboxes.temporary.label': 'POST /api/mailboxes - 临时邮箱',
+    'mailboxes.temporary.curl': `curl -X POST https://mail.example.com/api/mailboxes -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' -H 'Content-Type: application/json' -d '{
+  "mode": "temporary",
+  "domain": "example.com",
+  "expiresInMinutes": 60
+}'`,
+    'mailboxes.list.curl': "curl -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' https://mail.example.com/api/mailboxes",
+    'mailboxes.responseLabel': '201 创建响应',
+    'mailboxes.response': `{
+  "mailbox": {
+    "address": "tmp-a1b2c3@example.com",
+    "temporary": true,
+    "expiresAt": "2026-07-15T12:00:00.000Z"
+  },
+  "password": "&lt;GENERATED_MAILBOX_PASSWORD&gt;",
+  "clientConfig": {
+    "incoming": { "protocol": "IMAP", "host": "mail.example.com", "port": 993, "security": "IMAPS" },
+    "pop3": { "protocol": "POP3", "host": "mail.example.com", "port": 995, "security": "POP3S" },
+    "outgoing": { "protocol": "SMTP", "host": "mail.example.com", "port": 465, "security": "SMTPS" }
+  }
+}`,
+    'mailboxes.lifecycle.password': '自动生成的密码仅在创建响应中返回一次;请立即保存。',
+    'mailboxes.lifecycle.temporary': '临时邮箱在指定时间后到期,随后停止收信和认证。',
+    'mailboxes.lifecycle.list': '使用 mailboxes:read 查询邮箱元数据;列表接口永不返回密码。',
+    'receiving.title': '收信',
+    'receiving.sub': '邮件通过域名 MX 记录投递。在 MailHub 收件箱或标准 IMAP/POP3 客户端中读取;当前没有单独的 Bearer 收信读取 API。',
+    'receiving.delivery.title': '邮件投递到 MailHub',
+    'receiving.delivery.body': '将域名 MX 指向收信主机。外部邮件服务器会投递到 SMTP 25 端口;MailHub 将邮件存入已有邮箱、别名或未知地址收取目标。',
+    'receiving.routing.title': '邮箱、别名与未知地址收取',
+    'receiving.routing.body': '先创建完整邮箱地址,再在邮箱上添加别名或转发。通过控制台的域名未知地址收取设置,将不存在的本地部分路由到某个邮箱或外部地址。',
+    'receiving.protocols.protocol': '协议',
+    'receiving.protocols.ports': '推荐端口',
+    'receiving.protocols.security': '加密',
+    'receiving.protocols.use': '用途',
+    'receiving.protocols.imapSecurity': 'SSL/TLS 或 STARTTLS',
+    'receiving.protocols.imapUse': '推荐,用于同步文件夹',
+    'receiving.protocols.pop3Security': 'SSL/TLS 或 STLS',
+    'receiving.protocols.pop3Use': '下载到本地的客户端',
+    'receiving.protocols.smtpSecurity': 'SMTPS 或 STARTTLS',
+    'receiving.protocols.smtpUse': '使用相同邮箱地址和密码发信',
+    'receiving.folders': 'IMAP 会提供 INBOX、Sent、Drafts、Trash、Junk 和 Archive。需要同步这些文件夹并保存已发送邮件时,请优先使用 IMAP。',
     'domainsDoc.title': '域名配置',
     'domainsDoc.sub': '先在 MailHub 中添加域名。控制台 DNS 面板展示的验证、DKIM、SPF、DMARC 与发信主机值是该域名的最终准则。',
     'domainsDoc.col.purpose': '用途',
@@ -124,7 +194,28 @@ const messages = {
     'domainsDoc.note': '为提高投递可靠性,还需将发信 IP 的 PTR 配置为 MailHub 发信主机名,并在防火墙放行文档所列 SMTP、IMAP、POP3 端口。',
     'smtp.title': 'SMTP Submission',
     'smtp.sub': '默认监听端口(部署时可覆盖):',
-    'smtp.note': 'SMTP、IMAP、POP3 均使用完整邮箱地址和密码。优先使用 465、993、995 TLS 端口;必要时在普通端口启用 STARTTLS/STLS。',
+    'smtp.app.title': '应用 SMTP',
+    'smtp.app.body': '在控制台创建 SMTP 登录凭据供应用使用,并搭配属于该账号的 From 域名。',
+    'smtp.mailbox.title': '邮箱 SMTP',
+    'smtp.mailbox.body': '创建出的邮箱也可以用完整地址和密码通过 SMTP 认证发信;IMAP 与 POP3 使用相同的凭据。',
+    'smtp.note': '发信优先使用 465 或 587,IMAP 使用 993,POP3 使用 995。若部署覆盖了默认端口或 TLS 监听,请以创建邮箱响应中的 clientConfig 为准。',
+    'errors.title': '响应与常见错误',
+    'errors.sub': 'API 返回 JSON。使用响应体前,请先判断 HTTP 状态码。',
+    'errors.status': '状态码',
+    'errors.meaning': '含义',
+    'errors.action': '处理方式',
+    'errors.201.meaning': '邮箱已创建',
+    'errors.201.action': '保存一次性密码与 clientConfig。',
+    'errors.202.meaning': '邮件已被接受并交给 SMTP 投递',
+    'errors.202.action': '在日志或 Webhook 中查看后续投递状态。',
+    'errors.400.meaning': '请求参数不正确,或不满足域名归属规则',
+    'errors.400.action': '阅读 JSON error 字段,修正请求体或域名配置。',
+    'errors.401.meaning': 'Token 缺失、无效、已撤销或已过期',
+    'errors.401.action': '创建或轮换一个有效 Token。',
+    'errors.403.meaning': 'Token 缺少所需权限',
+    'errors.403.action': '在控制台 API Token 页面授予最小所需权限。',
+    'errors.409.meaning': '邮箱地址已存在',
+    'errors.409.action': '换用其他地址,或使用现有邮箱。',
     'webhooks.title': '投递 Webhooks',
     'webhooks.sub': '仅终态事件 —— 向你的系统发送签名 HTTPS 回调。',
     'webhooks.col.status': '状态',
@@ -140,6 +231,7 @@ const messages = {
     'nav.quickstart': 'Quick start',
     'nav.api': 'API',
     'nav.mailboxes': 'Mailboxes',
+    'nav.receiving': 'Receiving',
     'nav.domains': 'Domain setup',
     'nav.smtp': 'SMTP',
     'nav.webhooks': 'Webhooks',
@@ -149,6 +241,8 @@ const messages = {
     'cta.github': 'GitHub',
     'common.copy': 'Copy',
     'common.copied': 'Copied',
+    'common.yes': 'Yes',
+    'common.no': 'No',
     'hero.eyebrow': 'Transactional email platform',
     'hero.title': 'Send reliable email via API & SMTP',
     'hero.lede': 'Self-hosted MailHub gives you domain DNS guidance, DKIM signing, delivery logs, and signed webhooks — without locking you into a black-box SaaS.',
@@ -204,8 +298,15 @@ const messages = {
     'quickstart.client.title': 'Connect your client',
     'quickstart.client.body': 'Use SMTP to send and IMAP or POP3 to receive with the full mailbox address and password.',
     'quickstart.client.link': 'Review protocol settings →',
-    'api.title': 'Send API',
-    'api.sub': 'Minimal JSON contract. Hostnames below are placeholders — replace with your MailHub host.',
+    'api.title': 'API authentication and sending',
+    'api.sub': 'All public API calls use a Bearer token. The hostname below is a placeholder; replace it with your MailHub host.',
+    'api.auth.title': 'Create and protect a token',
+    'api.auth.body': 'Create a token in Console > API Tokens. The complete secret is shown only once, so store it outside browser code and public repositories.',
+    'api.auth.send': 'send messages with POST /api/send',
+    'api.auth.read': 'list mailboxes with GET /api/mailboxes',
+    'api.auth.write': 'create mailboxes with POST /api/mailboxes',
+    'api.send.title': 'Send a message',
+    'api.send.body': 'The From domain must belong to the token account. When verified sending is enabled, complete the domain DNS checks before sending.',
     'api.curl': `curl -X POST https://mail.example.com/api/send \\
   -H 'Authorization: Bearer &lt;USER_API_TOKEN&gt;' \\
   -H 'Content-Type: application/json' \\
@@ -215,16 +316,33 @@ const messages = {
     "subject": "Hello from MailHub",
     "text": "Signed with DKIM and queued by MailHub."
   }'`,
-    'api.field.from': 'sender address on a verified domain',
-    'api.field.to': 'recipient string or array',
-    'api.field.subject': 'message subject',
-    'api.field.body': 'plain text or HTML body',
+    'api.fields.name': 'Field',
+    'api.fields.required': 'Required',
+    'api.fields.description': 'Description',
+    'api.field.from': 'sender address on an account-owned domain',
+    'api.field.to': 'recipient string or an address array',
+    'api.field.subject': 'defaults to (no subject)',
+    'api.field.text': 'plain-text body',
+    'api.field.html': 'HTML body; required when enabling open or click tracking',
+    'api.field.tracking': 'true/false, or { opens, clicks }; rewrites HTML only',
+    'api.field.relay': 'an account-owned outbound SMTP relay ID',
+    'api.responseLabel': '202 response',
+    'api.response': `{
+  "eventId": 42,
+  "queued": true,
+  "domain": "example.com",
+  "recipients": ["user@example.com"],
+  "smtp": "Message queued",
+  "queueId": "AB12CD34",
+  "tracking": { "enabled": false, "opens": false, "clicks": false }
+}`,
+    'api.note': 'A 202 response means MailHub accepted the message for SMTP delivery. Use the console delivery log or webhooks to observe the later result.',
     'mailboxes.title': 'Mailbox API and clients',
     'mailboxes.sub': 'Create a persistent address for ongoing work, or an expiring address for a short-lived workflow.',
-    'mailboxes.api.title': 'Create a mailbox',
+    'mailboxes.api.title': 'Mailbox API scope',
     'mailboxes.api.body': 'POST to /api/mailboxes with a token holding mailboxes:write. The target domain must belong to that account.',
     'mailboxes.api.mode': 'permanent or temporary',
-    'mailboxes.api.address': 'use a full address, or let a temporary mailbox generate one from domain',
+    'mailboxes.api.address': 'use a full address, or choose domain plus localPart',
     'mailboxes.api.expires': 'required for temporary mailboxes; 5 minutes to 30 days',
     'mailboxes.api.password': 'optional; a secure password is returned once when omitted',
     'mailboxes.client.title': 'Use the returned configuration',
@@ -232,15 +350,58 @@ const messages = {
     'mailboxes.client.imap': 'recommended for synchronized folders and sent mail',
     'mailboxes.client.pop3': 'for download-oriented clients',
     'mailboxes.client.smtp': 'use the same full mailbox address and password to send',
-    'mailboxes.curl': `curl -X POST https://mail.example.com/api/mailboxes \\
-  -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' \\
-  -H 'Content-Type: application/json' \\
-  -d '{
-    "mode": "temporary",
-    "domain": "example.com",
-    "expiresInMinutes": 60
-  }'`,
-    'mailboxes.note': 'Use GET /api/mailboxes with mailboxes:read to list the account\'s mailboxes. A temporary mailbox stops accepting mail and authenticating at its expiration time.',
+    'mailboxes.permanent.label': 'POST /api/mailboxes - permanent',
+    'mailboxes.permanent.curl': `curl -X POST https://mail.example.com/api/mailboxes -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' -H 'Content-Type: application/json' -d '{
+  "mode": "permanent",
+  "address": "support@example.com",
+  "password": "&lt;MAILBOX_PASSWORD&gt;",
+  "displayName": "Support",
+  "aliases": ["help"],
+  "forwardTo": ["archive@example.net"],
+  "keepForwarded": true,
+  "quotaMb": 1024
+}'`,
+    'mailboxes.temporary.label': 'POST /api/mailboxes - temporary',
+    'mailboxes.temporary.curl': `curl -X POST https://mail.example.com/api/mailboxes -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' -H 'Content-Type: application/json' -d '{
+  "mode": "temporary",
+  "domain": "example.com",
+  "expiresInMinutes": 60
+}'`,
+    'mailboxes.list.curl': "curl -H 'Authorization: Bearer &lt;MAILBOX_API_TOKEN&gt;' https://mail.example.com/api/mailboxes",
+    'mailboxes.responseLabel': '201 creation response',
+    'mailboxes.response': `{
+  "mailbox": {
+    "address": "tmp-a1b2c3@example.com",
+    "temporary": true,
+    "expiresAt": "2026-07-15T12:00:00.000Z"
+  },
+  "password": "&lt;GENERATED_MAILBOX_PASSWORD&gt;",
+  "clientConfig": {
+    "incoming": { "protocol": "IMAP", "host": "mail.example.com", "port": 993, "security": "IMAPS" },
+    "pop3": { "protocol": "POP3", "host": "mail.example.com", "port": 995, "security": "POP3S" },
+    "outgoing": { "protocol": "SMTP", "host": "mail.example.com", "port": 465, "security": "SMTPS" }
+  }
+}`,
+    'mailboxes.lifecycle.password': 'The returned password is the only opportunity to read an automatically generated password.',
+    'mailboxes.lifecycle.temporary': 'A temporary mailbox expires after the requested interval and then stops receiving mail and authenticating.',
+    'mailboxes.lifecycle.list': 'Use mailboxes:read to list mailbox metadata; passwords are never returned by the list endpoint.',
+    'receiving.title': 'Receiving mail',
+    'receiving.sub': 'Mail arrives through the domain MX record. Read it in MailHub Inbox or through standard IMAP/POP3 clients; there is no separate inbound Bearer API.',
+    'receiving.delivery.title': 'Inbound delivery',
+    'receiving.delivery.body': 'Set the domain MX record to the receiving host. External mail servers then deliver to SMTP port 25, and MailHub stores mail for an existing mailbox, alias, or catch-all target.',
+    'receiving.routing.title': 'Mailbox, aliases, and catch-all',
+    'receiving.routing.body': 'Create a full mailbox address first. Add aliases or forwarding on that mailbox, and configure the domain catch-all in the console to route unknown local parts to a mailbox or another address.',
+    'receiving.protocols.protocol': 'Protocol',
+    'receiving.protocols.ports': 'Recommended ports',
+    'receiving.protocols.security': 'Security',
+    'receiving.protocols.use': 'Use',
+    'receiving.protocols.imapSecurity': 'SSL/TLS or STARTTLS',
+    'receiving.protocols.imapUse': 'recommended for synchronized folders',
+    'receiving.protocols.pop3Security': 'SSL/TLS or STLS',
+    'receiving.protocols.pop3Use': 'download-oriented clients',
+    'receiving.protocols.smtpSecurity': 'SMTPS or STARTTLS',
+    'receiving.protocols.smtpUse': 'send with the same mailbox address and password',
+    'receiving.folders': 'IMAP exposes INBOX, Sent, Drafts, Trash, Junk, and Archive. Use IMAP when the client should synchronize these folders and save sent messages.',
     'domainsDoc.title': 'Domain configuration',
     'domainsDoc.sub': 'Create the domain in MailHub first. Its DNS panel is the source of truth for your exact verification, DKIM, SPF, DMARC, and sender-host values.',
     'domainsDoc.col.purpose': 'Purpose',
@@ -256,7 +417,28 @@ const messages = {
     'domainsDoc.note': 'For reliable delivery, also configure the sending IP\'s PTR to match the MailHub sending hostname and allow the documented SMTP, IMAP, and POP3 ports through your firewall.',
     'smtp.title': 'SMTP submission',
     'smtp.sub': 'Default listener ports (override in your deployment):',
-    'smtp.note': 'Use a mailbox\'s full address and password for SMTP, IMAP, and POP3. Prefer TLS ports 465, 993, and 995; use STARTTLS/STLS on the plain ports when needed.',
+    'smtp.app.title': 'Application SMTP',
+    'smtp.app.body': 'Create an SMTP login credential in the console for an application. Use that credential with an account-owned From domain.',
+    'smtp.mailbox.title': 'Mailbox SMTP',
+    'smtp.mailbox.body': 'A created mailbox can also authenticate over SMTP with its full email address and password. Use the same credentials for IMAP or POP3.',
+    'smtp.note': 'Prefer 465 or 587 for sending, 993 for IMAP, and 995 for POP3. The creation response is the source of truth when a deployment overrides default ports or TLS listeners.',
+    'errors.title': 'Responses and common errors',
+    'errors.sub': 'The API returns JSON. Validate the HTTP status before consuming a response body.',
+    'errors.status': 'Status',
+    'errors.meaning': 'Meaning',
+    'errors.action': 'What to do',
+    'errors.201.meaning': 'Mailbox created',
+    'errors.201.action': 'Store the one-time password and clientConfig.',
+    'errors.202.meaning': 'Message accepted for SMTP delivery',
+    'errors.202.action': 'Inspect delivery logs or webhooks for later delivery status.',
+    'errors.400.meaning': 'Invalid request or domain ownership rule',
+    'errors.400.action': 'Read the JSON error and correct the request body or domain setup.',
+    'errors.401.meaning': 'Missing, invalid, revoked, or expired token',
+    'errors.401.action': 'Create or rotate a valid token.',
+    'errors.403.meaning': 'Token does not have the required scope',
+    'errors.403.action': 'Grant the minimum required scope in Console > API Tokens.',
+    'errors.409.meaning': 'Mailbox address already exists',
+    'errors.409.action': 'Choose another address or use the existing mailbox.',
     'webhooks.title': 'Delivery webhooks',
     'webhooks.sub': 'Terminal events only — signed HTTPS callbacks to your systems.',
     'webhooks.col.status': 'Status',

+ 16 - 0
src/frontend/landing/landing.css

@@ -370,6 +370,21 @@ main {
   margin-top: 16px;
 }
 
+.reference-grid {
+  display: grid;
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+  gap: 16px;
+}
+
+.reference-grid .doc-code-card {
+  margin-top: 16px;
+}
+
+.compact-note {
+  margin-top: 12px;
+  overflow-wrap: anywhere;
+}
+
 .port-grid {
   display: grid;
   grid-template-columns: repeat(4, minmax(0, 1fr));
@@ -489,6 +504,7 @@ tr:last-child td {
   .integrate-grid,
   .steps-grid,
   .doc-grid,
+  .reference-grid,
   .port-grid {
     grid-template-columns: 1fr;
   }

+ 4 - 0
test/server-landing.test.js

@@ -19,6 +19,10 @@ test('anonymous root serves landing page with no-store cache header', async () =
     assert.match(html, /MailHub/i);
     assert.match(html, /data-i18n|hero|Get started|开始使用|landing/i);
     assert.match(html, /api\/mailboxes|mailboxes|邮箱/i);
+    assert.match(html, /mailboxes:write|长期邮箱|permanent/i);
+    assert.match(html, /expiresInMinutes|临时邮箱|temporary/i);
+    assert.match(html, /Receiving mail|收信|IMAP|POP3/i);
+    assert.match(html, /POST \/api\/send|发送 API|Send API/i);
     assert.match(html, /domainsDoc|域名配置|Domain configuration/i);
     assert.doesNotMatch(html, /id="root"/);
   } finally {

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini