> 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/email-provider/adobe-marketing-cloud/adobe-campaign-transactional-messaging.md).

# Adobe Campaign Transactional Messaging

Send The Wallet Crew transactional emails via Adobe Campaign Transactional Messaging using the script email provider.

Use this option when the organization runs **Adobe Campaign** and already uses **Transactional Messaging** for production sends.

Before going further, make sure the script provider model used by The Wallet Crew is clear. Start with [Adobe Marketing Cloud](/connectors/email-provider/adobe-marketing-cloud.md) and the prerequisite [EmailSender extensibility](/connectors/custom-connector/emailsender-extensibility.md).

### When to use this

This is a good fit when Adobe Campaign governance, reporting, and pre-existing transactional events are already in place.

### Setup required in Adobe

Create and publish a **Transactional Message Event**.

Then decide whether Adobe Campaign will use the rendered HTML from The Wallet Crew or build the final email using Adobe Campaign templates and variables.

A secure server-side authentication method supported by the Adobe Campaign deployment is also required. This is often based on Adobe I/O or IMS.

### Implementation notes (event call)

The script usually renders the email (when The Wallet Crew controls the HTML), then calls the transactional event endpoint with the recipient and context.

Store these values as secrets before testing:

* `adobe-clientId`
* `adobe-clientSecret`
* `adobe-campaign-transactionalUrl` (full Transactional Message Event URL)
* `adobe-orgId` (optional, depends on the Campaign setup)
* `adobe-scope` (optional, depends on the Adobe setup)

{% code title="Adobe Campaign example (SendEmail implementation)" %}

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

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

// Adobe IMS (OAuth2 client credentials)
const IMS_TOKEN_URL = "https://ims-na1.adobelogin.com/ims/token/v3";

async function getOptionalSecret(name, fallback) {
  try {
    return (await getSecret(name)) || fallback;
  } catch (e) {
    return fallback;
  }
}

/**
 * @typedef {Object} EmailData
 * @property {string} Subject
 * @property {string} Body
 */

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)}`;

  // Keep Wallet Crew rendering available.
  // If Adobe Campaign owns the template, you can remove this and only send variables.
  const rendered = await buildEmail(emailTemplate);

  const transactionalUrl = await getSecret("adobe-campaign-transactionalUrl");

  const clientId = await getSecret("adobe-clientId");
  const clientSecret = await getSecret("adobe-clientSecret");
  const scope = await getOptionalSecret("adobe-scope", "openid,AdobeID");

  // Optional. Some setups require org id headers, others do not.
  const orgId = await getOptionalSecret("adobe-orgId", null);

  const headers = {
    "x-api-key": clientId,
    "Content-Type": "application/json",
  };
  if (orgId) headers["x-gw-ims-org-id"] = orgId;

  const result = await fetch(transactionalUrl, {
    Method: "POST",
    Authentication: {
      mode: "OAuth2.0",
      grantType: "clientCredentials",
      sendCredentialsAsFormParams: true,
      accessTokenUrl: IMS_TOKEN_URL,
      clientId,
      clientSecret,
      scope,
    },
    Headers: headers,
    Body: JSON.stringify({
      // Payload must match your Transactional Message Event definition.
      email,
      ctx: {
        url,
        language,

        // Optional if you want Campaign to use Wallet Crew rendering:
        subject: rendered.Subject,
        body: rendered.Body,

        // Useful metadata:
        template: emailTemplate,
        customerId,
      },
    }),
    ThrowOnError: false,
  });

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

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

{% endcode %}

### What to validate

Trigger a real transactional email, then validate these points:

* The Transactional Message Event is published and callable.
* The payload schema matches the event definition.
* Adobe Campaign accepts the request and processes the message.

### FAQ

<details>

<summary><strong>Is a Transactional Message Event required?</strong></summary>

Yes. The script must call a published Transactional Messaging Event endpoint. That event defines the expected payload schema and which variables are available in the Adobe Campaign template.

</details>

<details>

<summary><strong>Can Adobe Campaign render the email instead of The Wallet Crew?</strong></summary>

Yes. In that setup, the script sends only the variables (for example, the pass download URL and customer metadata). Adobe Campaign owns the HTML, subject, and translations.

</details>

<details>

<summary><strong>Which authentication method should be used?</strong></summary>

It depends on the Adobe Campaign deployment and how the Adobe organization manages server-to-server access. This page shows an IMS-based approach because it is common, but the final authentication method should be aligned with the Adobe admin team and the official Adobe documentation for that environment.

</details>

<details>

<summary><strong>What is the most common reason for request rejection?</strong></summary>

Schema mismatch. The event definition is strict. When payload fields do not match the expected structure of the event, Campaign rejects or ignores fields, and templates render empty values.

</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/email-provider/adobe-marketing-cloud/adobe-campaign-transactional-messaging.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.
