> For the complete documentation index, see [llms.txt](https://docs.thewalletcrew.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thewalletcrew.io/connectors/custom-connector/emailsender-extensibility.md).

# EmailSender extensibility

Implement a script-based email provider by registering \`runtime.scriptable.emailEngine.SendEmail\`.

### What this is

Use this extensibility point to have The Wallet Crew send emails through a provider under tenant control. This is a script-level integration. It keeps The Wallet Crew templates and rendering logic, while delegating the final “send” to dedicated API calls.

This design separates rendering from delivery. That makes the email layer easier to test and easier to swap. It also keeps culture handling explicit, because the caller decides which cultures exist and how templates are resolved.

### How `SendEmail` works

The platform calls `runtime.scriptable.emailEngine.SendEmail` when it needs to send an email and the email provider is set to `script`. The function receives the recipient address, the template name, a data payload, and the list of supported cultures.

The last parameter, `buildEmail`, is a callback supplied by the platform. The provider implementation calls it to obtain the final rendered content. That callback returns an object with a `Subject` and a `Body`. Once that payload is available, the script is responsible for sending it.

### Reference implementation (signature)

```js
/**
 * @typedef {Object} EmailData
 * @property {string} Subject - Rendered subject line.
 * @property {string} Body - Rendered body content.
 */

/**
 * Send an email using the provided template name and data.
 * The caller supplies the supported cultures and a callback
 * to build the rendered email content.
 *
 * @param {string} recipient - Target email address.
 * @param {string} emailTemplate - Template name to render.
 * @param {Object.<string, any>} data - Template data passed to the script.
 * @param {string[]} cultures - Available culture names from the culture provider.
 * @param {(templateName: string) => Promise<EmailData>} buildEmail
 * Callback that returns the final rendered email content.
 *
 * @returns {Promise<void>}
 */
async function sendEmail(recipient, emailTemplate, data, cultures, buildEmail){
   // call any API to send email
}

export default function(context) {
  context.register('runtime.scriptable.emailEngine', {
    SendEmail: sendEmail 
  })
}
```

`SendEmail` runs in the same runtime as any other custom connector script, so `fetch`, `getSecret`, and the rest of the approved globals are available for the actual delivery call. See [Custom connectors → Runtime constraints](/developers-guides/integration-guides/custom-connectors.md) for the full list and the 5-second execution limit that applies here as well.

### Enable the script email provider

Switching to the script email provider is a configuration change. It tells The Wallet Crew to call the tenant `SendEmail` implementation for outgoing emails.

{% stepper %}
{% step %}

#### Edit `/server/emails.yml`

Open the advanced configuration editor, then create or edit `/server/emails.yml`. Set the provider type to `script`. Keep `resources` pointing to the email template directory.
{% endstep %}
{% endstepper %}

```yaml
provider:
  type: script
resources:
  - /locales/emails/

```

### Template and culture notes

Email templates are loaded from the paths listed under `resources`. Keep this aligned with where the HTML templates live, because the rendering callback uses those templates.

Culture handling is part of the contract. The platform passes a `cultures` array, but the provider implementation decides how to use it. In practice, most setups either pick a single culture per recipient or fall back to a default culture when no specific template exists.

### Troubleshooting

If emails stop sending after enabling the script provider, start by confirming that `/server/emails.yml` is valid YAML and saved in the right tenant. Then confirm the script is actually registered under `runtime.scriptable.emailEngine` and exports `SendEmail`.

For provider-side errors, log the template name and culture choices, but avoid logging full rendered bodies. Email content can contain personal data.

### FAQ

<details>

<summary><strong>Is this a public HTTP endpoint?</strong></summary>

No. This page documents a script runtime interface that the tenant script implements. An external HTTP API is called from inside `SendEmail`.

</details>

<details>

<summary><strong>Where do email templates live?</strong></summary>

They live under the directories listed in `resources`. By default, this points to `/locales/emails/`.

</details>

<details>

<summary><strong>Can a script provider be mixed with SendGrid or Mailchimp?</strong></summary>

Not at the same time for a given tenant. The active provider is selected in `/server/emails.yml`.

</details>

<details>

<summary><strong>What should <code>SendEmail</code> return?</strong></summary>

Return normally when the email is accepted by the provider. Throw an error when sending fails, so the platform can surface it.

</details>

<details>

<summary><strong>What is the fastest way to validate the setup?</strong></summary>

Enable the `script` provider, trigger a single transactional email, then check the provider logs for the send event.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.thewalletcrew.io/connectors/custom-connector/emailsender-extensibility.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
