// integrations

Resend

Scan inbound Resend email with Context Guardrails before your application processes phishing, credential harvesting, or social engineering.

Resend is an email API for developers. Wire Context Guardrails into Resend's inbound webhook pipeline to scan every incoming message for phishing, credential harvesting, prompt injection, and social engineering before your application processes it.

Prerequisites

How it works

flowchart TD
    A[Incoming email] --> B[Resend inbound]
    B --> C["email.received webhook"]
    C --> D["Fetch raw email via Receiving API"]
    D --> E["POST /api/v1/context/email"]
    E -->|safe or caution| F[Process email]
    E -->|suspicious or dangerous| G[Discard / alert]

Resend fires an email.received webhook when a message arrives at your receiving domain. The webhook payload contains metadata only (from, to, subject, email_id) — not the email body. Your handler fetches the raw email via Resend's Receiving API and POSTs it to Superagent's /context/email endpoint.

Sending the raw RFC 822 content preserves headers, MIME structure, and authentication signals (SPF, DKIM) for more accurate phishing and identity checks. If the verdict is suspicious or dangerous, the handler discards the email or triggers an alert. Treat caution as review-before-use.

Setup

1. Configure an email.received webhook

In the Resend dashboard, add a webhook subscribed to the email.received event pointing at your handler URL. Verify webhook signatures before you trust the payload. You can also create the webhook via the SDK:

import resend

resend.api_key = "re_..."

resend.Webhooks.create({
    "url": "https://your-server.com/webhooks/resend",
    "events": ["email.received"],
})
import { Resend } from "resend";

const resend = new Resend("re_...");

await resend.webhooks.create({
  url: "https://your-server.com/webhooks/resend",
  events: ["email.received"],
});

2. Build a webhook handler that scans with Superagent

When an email.received event arrives, fetch the raw email from the Receiving API and POST it to Superagent. Use mode=full so the handler waits for the completed score (up to 90 seconds) instead of a preliminary identity result. Set your HTTP client timeout accordingly.

If your platform's webhook timeout is shorter than that, POST without mode=full, return 200 to Resend, and poll GET /api/v1/context/email/{identifier} until pending_deep_scan is false.

import os

import httpx
import resend
from fastapi import FastAPI, Request

app = FastAPI()
resend.api_key = os.environ["RESEND_API_KEY"]

@app.post("/webhooks/resend")
async def handle_email_received(request: Request):
    event = await request.json()
    if event.get("type") != "email.received":
        return {"status": "ignored"}

    email = resend.Emails.Receiving.get(email_id=event["data"]["email_id"])
    download_url = (email.get("raw") or {}).get("download_url")
    if not download_url:
        return {"status": "error", "message": "raw email not available"}

    async with httpx.AsyncClient(timeout=90.0) as http:
        eml = await http.get(download_url)
        eml.raise_for_status()

        scan = await http.post(
            "https://superagent.sh/api/v1/context/email?mode=full&details=true",
            content=eml.content,
            headers={
                "Authorization": f"Bearer {os.environ['SUPERAGENT_API_KEY']}",
                "Content-Type": "message/rfc822",
            },
        )
        scan.raise_for_status()

    artifact = scan.json()["data"]
    verdict = artifact.get("verdict", "safe")

    if verdict in ("suspicious", "dangerous"):
        print(
            f"Blocked email from {email.get('from')} — "
            f"verdict: {verdict}, score: {artifact.get('score')}"
        )
        return {"status": "blocked", "verdict": verdict}

    # Deliver the message to your agent or application.
    return {"status": "processed"}
import { Resend } from "resend";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

const resend = new Resend(process.env.RESEND_API_KEY);

export const POST = async (request: NextRequest) => {
  const event = await request.json();
  if (event.type !== "email.received") {
    return NextResponse.json({ status: "ignored" });
  }

  const { data: email } = await resend.emails.receiving.get(
    event.data.email_id,
  );
  const downloadUrl = email?.raw?.download_url;
  if (!downloadUrl) {
    return NextResponse.json(
      { status: "error", message: "raw email not available" },
      { status: 500 },
    );
  }

  const eml = await fetch(downloadUrl);
  const emlBody = await eml.text();

  const scan = await fetch(
    "https://superagent.sh/api/v1/context/email?mode=full&details=true",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SUPERAGENT_API_KEY}`,
        "Content-Type": "message/rfc822",
      },
      body: emlBody,
      signal: AbortSignal.timeout(90_000),
    },
  );

  const payload = await scan.json();
  const artifact = payload.data;
  const verdict = artifact.verdict ?? "safe";

  if (verdict === "suspicious" || verdict === "dangerous") {
    console.log(
      `Blocked email from ${email.from} — verdict: ${verdict}, score: ${artifact.score}`,
    );
    return NextResponse.json({ status: "blocked", verdict });
  }

  // Deliver the message to your agent or application.
  return NextResponse.json({ status: "processed" });
};

Return 2xx for both processed and blocked messages so Resend does not retry a scan you already completed.

3. Deploy and test

Deploy your webhook handler and send a test email to your Resend receiving address. Check the logs to confirm Superagent is scanning each message and returning a verdict.

What gets detected

Context Guardrails checks sender identity, the body and attachments, and outbound links. See email scoring for the full scan model.

Threat Description
phishing Credential harvesting, urgency lures, or dangerous followed links
social_engineering Deceptive content designed to manipulate a person or agent
prompt_injection Hidden instructions aimed at an agent that reads the message
exfiltration Patterns designed to leak session data or secrets
brand_impersonation Message impersonates a known brand from an unrelated sender domain
malicious_attachment Dangerous attachment types
spf_fail / display_name_mismatch Sender authentication or display-name problems

Each threat is assigned a severity (critical, high, medium, low). Pass details=true to include threats even when the verdict is safe.

Using the response

The Superagent response follows the standard API envelope:

{
  "data": {
    "object": "context_artifact",
    "id": "00000000-0000-4000-8000-000000000001",
    "origin": "email",
    "identifier": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "url": "email:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "score": 15,
    "verdict": "dangerous",
    "confidence": "high",
    "tolerance": "conservative",
    "scanned_at": "2026-09-01T12:00:00.000Z",
    "pending_deep_scan": false,
    "threats": [
      {
        "type": "phishing",
        "severity": "critical",
        "detail": "Credential harvesting form targeting Google login"
      }
    ]
  }
}

Verdicts are safe, caution, suspicious, and dangerous. You can also read them from response headers (x-superagent-verdict, x-superagent-score, x-superagent-confidence, x-superagent-tolerance) for lightweight checks without parsing JSON.

Next steps