# Marketo Engage (Transactional Email API)

Use this option when your organization uses **Marketo Engage** and wants transactional sends tracked in Marketo activity logs.

Before you go further, make sure you understand how the script provider works in The Wallet Crew. Start with [Adobe Marketing Cloud](https://docs.thewalletcrew.io/connect/email-provider/adobe-marketing-cloud) and the prerequisite [EmailSender extensibility](https://docs.thewalletcrew.io/connect/custom-connector/emailsender-extensibility).

### When to use this

This is often the simplest REST model, but it assumes you manage and approve the email template in Marketo.

### Setup required in Marketo

Create the email asset you want to send and get it approved. Approval rules vary by Marketo setup.

Then decide how you map The Wallet Crew output:

* Use Marketo tokens or variables to inject `Subject` and `Body`.
* Or keep the subject and body in Marketo and only pass contextual data.

### Implementation notes (token + send)

Marketo instances are region-specific. Your script usually requests a Marketo access token, then calls the transactional send endpoint for the chosen email asset.

Store these values as secrets before you test:

* `marketo-baseUrl` (example: `https://123-ABC-456.mktorest.com`)
* `marketo-clientId`
* `marketo-clientSecret`
* `marketo-emailId` (the Marketo email asset identifier you send)

{% hint style="info" %}
Marketo OAuth token endpoints can differ slightly by instance and configuration. If your Marketo token endpoint requires query parameters or a specific HTTP method, adapt the authentication settings accordingly.
{% endhint %}

{% code title="Marketo Engage example (SendEmail implementation)" %}

```javascript
import { getSecret } from "neo/secrets";

const CUSTOM_DOMAIN = "wallet.brand.com";
const TENANTID = "brand";

async function sendEmail(recipient, emailTemplate, data, cultures, buildEmail) {
  const email = recipient;
  const countryCode = data["country"] || "fr";
  const language = `${cultures[0]?.toLowerCase()?.substring(0, 2) || "fr"}_${countryCode.toLowerCase()}`;
  const customerId = data["id.customerId"];

  if (!customerId) {
    throw new Error('Missing "id.customerId" in email data.');
  }

  const url = `https://${CUSTOM_DOMAIN}/${TENANTID}/pass?id.customerId=${encodeURIComponent(customerId)}`;
  const rendered = await buildEmail(emailTemplate);

  const baseUrl = await getSecret("marketo-baseUrl");
  const clientId = await getSecret("marketo-clientId");
  const clientSecret = await getSecret("marketo-clientSecret");
  const marketoEmailId = await getSecret("marketo-emailId");

  const sendUrl = `${baseUrl}/rest/v1/email/${encodeURIComponent(marketoEmailId)}/send.json`;

  // Your runtime `fetch` handles OAuth client credentials and attaches the token.
  const result = await fetch(sendUrl, {
    Method: "POST",
    Authentication: {
      mode: "OAuth2.0",
      grantType: "clientCredentials",
      sendCredentialsAsFormParams: true,
      accessTokenUrl: `${baseUrl}/identity/oauth/token`,
      clientId,
      clientSecret,
    },
    Headers: {
      "Content-Type": "application/json",
    },
    Body: JSON.stringify({
      input: [
        {
          email,
          variables: [
            { name: "url", value: url },
            { name: "language", value: language },
            { name: "subject", value: rendered.Subject },
            { name: "body", value: rendered.Body },
            { name: "customerId", value: customerId },
            { name: "template", value: emailTemplate },
          ],
        },
      ],
    }),
    ThrowOnError: false,
  });

  if (result.StatusCode < 200 || result.StatusCode >= 300) {
    throw new Error(`Marketo send call failed (${result.StatusCode}): ${result.ResponseText}`);
  }
}

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

{% endcode %}

{% hint style="info" %}
Marketo requires the email asset to exist (and often to be approved) before it can be used for sends.
{% endhint %}

### What to validate

Trigger a real transactional email, then validate these points:

* The email asset exists and is approved.
* Marketo accepts the send call.
* Variables map to what your Marketo template expects.

### FAQ

<details>

<summary><strong>Do we need to create the email template in Marketo?</strong></summary>

Yes. Marketo requires an existing email asset for transactional sends, and many setups require the asset to be approved. Your script can still pass variables, but Marketo needs the container asset.

</details>

<details>

<summary><strong>Can The Wallet Crew still render the HTML?</strong></summary>

Yes. You can call `buildEmail()` and pass `subject` and `body` as variables. Your Marketo email must be built to inject those variables into the right places.

</details>

<details>

<summary><strong>What’s the quickest way to troubleshoot?</strong></summary>

First check that the asset is approved, then check the Marketo API response body for a specific error code. If the API call succeeds but the email renders badly, validate that your Marketo template expects the same variable names you send.

</details>

<details>

<summary><strong>Can we use different Marketo instances per environment?</strong></summary>

Yes. Configure different secrets and endpoints per tenant (staging vs production). Keep variable naming consistent across environments to avoid template drift.

</details>
