---
title: Approve agent-requested email
description: Let a model propose content while the host owns sender, account, keys, policy and approval.
---

Use `EmailAgent` from `@corven/email-sdk/agent` to expose a constrained proposal boundary. Model input contains `to`, `subject` and `body`. The host supplies the sender and the configured `Email` service.

```ts
import { Effect, Layer } from "effect";
import type { Redacted } from "effect";
import { EmailAgent, EmailAgentApproval, emailAgentLayer } from "@corven/email-sdk/agent";
import type { IEmailAgentApproval } from "@corven/email-sdk/agent";
import { postmarkEmailLayer } from "@corven/email-sdk/postmark";

export const hostApprovedAgentLayer = (
  serverToken: Redacted.Redacted,
  review: IEmailAgentApproval["review"],
) =>
  emailAgentLayer({ from: { address: "host@fictional.invalid", name: "Fictional host" } }).pipe(
    Layer.provide(Layer.succeed(EmailAgentApproval, EmailAgentApproval.of({ review }))),
    Layer.provide(postmarkEmailLayer({ serverToken })),
  );

export const fictionalAgentSubmission = Effect.gen(function* submitHostReviewedNotification() {
  const agent = yield* EmailAgent;

  return yield* agent.submit({
    body: { text: "Fictional content for the host to review." },
    subject: "Fictional agent notification",
    to: "recipient@fictional.invalid",
  });
});
```

The account Layer still needs HTTP transport. The sample accepts a host review callback rather than automatically approving proposals.

## Review effective candidates

`EmailAgentApproval.review` receives owned `message`, `options` and all effective `previews`. Defaults, transformations and routes have already been prepared, so review the actual candidates rather than the model's earlier draft.

Return `Approved` only when host policy permits that exact operation. `Declined` fails with `EmailAgentApprovalError`; unavailable review must remain an explicit failure. The SDK validates and dispatches the same prepared operation after approval.

## Keep authority in the host

Do not accept model-supplied sender accounts, credentials, route policy or replay keys as authority. Framework approval flags, confirmation text and tool-call IDs do not replace `EmailAgentApproval`.

Approval does not prove account permission, submission or delivery. A failed or interrupted tool call can leave the outcome unknown, so do not retry automatically. Use [AI tools](/integrations/ai) for a framework bridge and [prepared operations](/composition) for scope and callback semantics.
