> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gavai.io/llms.txt
> Use this file to discover all available pages before exploring further.

# sendEmail

> Send a templated transactional email through the platform's transactional email gateway.

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const Def = ({term, type, defaultValue, required, children}) => <>
    <dt className="gv-def-list__term">
      <span className="gv-def-list__name">{term}</span>
      {type && <span className="gv-def-list__type">{type}</span>}
      {required && <span className="gv-def-list__required">required</span>}
      {defaultValue !== undefined && <span className="gv-def-list__default">
          default <code>{defaultValue}</code>
        </span>}
    </dt>
    <dd className="gv-def-list__description">{children}</dd>
  </>;

export const DefList = ({children}) => <dl className="gv-def-list">{children}</dl>;

export const ArrowItem = ({children}) => <li className="gv-arrow-item">
    <span className="gv-arrow-item__arrow" aria-hidden="true">{"↳︎"}</span>
    <span className="gv-arrow-item__content">{children}</span>
  </li>;

export const ArrowList = ({children}) => <ul className="gv-arrow-list">{children}</ul>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="CAPABILITY" />

`sendEmail` delivers a templated transactional email to a recipient through the platform's transactional email gateway. You reference a template by ID and pass variables to hydrate it; the platform proxies the send so you never touch gateway credentials or sending infrastructure. By the end of this page you can wire a Form block to send a confirmation email on submit, or fire a notification from any block with a capability hook.

## Invoke from a Form block

The canonical pattern: a Form block firing `sendEmail` on submit. Form-field bindings (the runtime's template-expression syntax) resolve against the submitted data before dispatch.

```json lead-form.json theme={null}
{
  "block": "Form",
  "props": {
    "schema_table": "leads",
    "fields": ["name", "email"],
    "on_submit": {
      "capability": "sendEmail",
      "args": {
        "template": "lead-thankyou",
        "to": "ada@acme.com",
        "variables": {
          "first_name": "Ada"
        }
      }
    }
  }
}
```

The runtime validates the args, renders `lead-thankyou` with the variables, hands the message to the email gateway, and returns a `message_id` you can use to track delivery.

## Inputs

<DefList>
  <Def term="template" type="string" required>
    The template ID as defined in the Builder's email templates UI, e.g. `"lead-thankyou"`.
  </Def>

  <Def term="to" type="string" required>Recipient email address.</Def>

  <Def term="variables" type="Record<string, string>">
    Key-value pairs substituted into the template at render time.
  </Def>

  <Def term="reply_to" type="string">
    Sets the `Reply-To` header on the outgoing message.
  </Def>
</DefList>

## Outputs

<DefList>
  <Def term="message_id" type="string">
    The gateway message ID assigned at queue time. Use it for delivery tracking and to correlate with bounce/complaint webhooks.
  </Def>

  <Def term="queued_at" type="string">
    ISO 8601 timestamp when the message was accepted by the gateway queue.
  </Def>
</DefList>

## Provisioning

The workspace must have a verified sending domain before `sendEmail` can dispatch. Verification is handled in the Builder's email settings — add a domain, publish the DNS records the Builder shows you, and wait for the gateway to confirm. Until verification completes, calls return `domain_not_verified`.

Email templates are defined in the Builder's template editor. Create and preview a template before referencing it by ID — invalid IDs return `template_not_found`.

## Error codes

<ErrorTable
  errors={[
{
  code: "template_not_found",
  cause: "No template with the given ID exists in the workspace.",
  fix: "Create or publish the template in the Builder, then reference its ID.",
},
{
  code: "domain_not_verified",
  cause: "The workspace's sending domain has not been verified with the email gateway.",
  fix: "Add the sending domain in email settings and publish the DNS records shown.",
},
{
  code: "invalid_input",
  cause: "One or more required fields failed schema validation.",
  fix: "Inspect `error.details` for field-level messages and correct the args.",
},
]}
/>

Every error returns the standard envelope:

<ExpectedOutput caption="Error envelope">
  ```json error-envelope.json theme={null}
  {
    "ok": false,
    "error": {
      "code": "template_not_found",
      "message": "Template 'lead-thankyou' not found in workspace.",
      "details": { "template": "lead-thankyou" }
    }
  }
  ```
</ExpectedOutput>

## Idempotency

`sendEmail` is **not** idempotent by default. A retry sends a second email. If you wire `on_submit` to `sendEmail`, debounce the submit button or use a server-side dedupe key in `variables` to prevent duplicate sends from double-clicks.

## Related

<Columns cols={2}>
  <Card title="storeFile" icon="file-arrow-up" href="/capabilities/store-file">
    Upload a file and return a signed or public URL.
  </Card>

  <Card title="Send email guide" icon="envelope" href="/guides/send-email">
    Verify a domain, author a template, and wire it to a form.
  </Card>
</Columns>
