Delivery Status Notifications

SMTP tells a sender what happened to their message with a bounce. A bridge does the same with a kind 7679 event, without telling the relays who it is talking to.


The event

{
  "kind": 7679,
  "pubkey": "<bridge_permanent_pubkey>",
  "tags": [
    ["r", "<email_id>"],
    ["ephemeral-pubkey", "<temp_pubkey>"]
  ],
  "content": "<nip44_encrypted_content>"
}
Tag Required Description
r Yes The email-id of the original kind 1301 event
ephemeral-pubkey Yes Temporary public key, hex, used to decrypt the content

Why it is shaped this way

Threat Protection
Relays see who sent the original No p tag, so the event names nobody
The bridge reads its own archive The ephemeral private key is destroyed, leaving ciphertext
Delivery failures end up in logs The content is encrypted, so the logs say nothing
A forged notification The bridge's permanent key signs the event

The sender is found through the r tag: only someone who knows the email-id of an email they sent can ask for its notifications.


Content

NIP-44 encrypted JSON:

{
  "status": "delivered",
  "recipient": "bob@example.com",
  "smtp_code": 250,
  "smtp_response": "2.1.5 OK",
  "attempted_at": 1735401600,
  "delivered_at": 1735401605,
  "details": "Email successfully delivered to recipient's SMTP server"
}
Field Type Description
status string delivered, failed, delayed or queued
recipient string The recipient's email address
smtp_code number The SMTP response code
smtp_response string The full SMTP response
attempted_at number When delivery was attempted
delivered_at number Present on success
failed_at number Present on failure
details string Human-readable explanation
Status SMTP codes Meaning
delivered 250, 2xx Accepted by the recipient's server
failed 5xx Permanent failure: no such mailbox, refused, full
delayed 4xx Temporary: greylisting, rate limiting
queued none Accepted by the bridge, delivery still to come

Sending one, as a bridge

  1. Generate an ephemeral keypair.

  2. Encrypt the JSON with NIP-44 using the ephemeral private key and the sender's pubkey.

  3. Build the kind 7679 event with the r and ephemeral-pubkey tags, signed by the bridge's permanent key.

  4. Destroy the ephemeral private key. Never write it to disk.

  5. Publish.


Reading one, as a sender

{
  "authors": ["<bridge_pubkey>"],
  "kinds": [7679],
  "#r": ["<email_id>"]
}

Take temp_pubkey from the ephemeral-pubkey tag, decrypt the content with NIP-44 using your private key and that pubkey, and read the JSON.

This is why the email-id tag matters on the way out: without it, there is nothing for a notification to point at.