Trello can become a useful operational trigger for customer and team communication—but to send email from Trello safely, you need a server-side webhook integration rather than a browser-side shortcut. Volanea does not provide a native Trello Power-Up or Marketplace app, so the production-ready path is Trello webhook → your secure endpoint → Volanea REST API.

What this Trello email integration does

This integration sends an email when a Trello card is moved to a specific list. In Trello’s event model, moving a card is an updateCard action. The webhook payload includes the action that happened and the board model being watched, which lets your application decide whether that move should produce an email.

A practical example is a customer-success board with these lists:

  • New request
  • Reviewing
  • Waiting for customer
  • Ready to send
  • Complete

When a teammate moves a card into Waiting for customer, your endpoint can send a customer-success notification to an internal mailbox. When a card moves into Ready to send, it can notify an operations team that a deliverable needs final approval. The same pattern works for onboarding milestones, content approvals, incident escalation, recruiting pipelines, or support handoffs.

The key distinction is that Trello is the event source, not the email sender. Trello sends an HTTP POST to an endpoint you control. That endpoint verifies the request, translates Trello’s event data into an email payload, and calls Volanea over HTTPS.

This gives you several benefits that a manual email workflow cannot:

  1. Explicit business rules. You decide exactly which board, list movement, card name pattern, or label should send an email.
  2. Secure credentials. Your Volanea API key stays in server-side environment variables instead of appearing in a Trello card, a client app, or an automation field visible to users.
  3. Duplicate protection. Trello delivery retries and your own retry logic do not need to create multiple copies of the same email.
  4. Reliable observability. You can log the Trello action ID, Volanea response, and final email status together.
  5. A path to more advanced workflows. The first card-move alert can later become a template-based notification, a recipient lookup, or a multi-step workflow.

The real Trello trigger: a card moved to a list

For this guide, the concrete trigger is:

A Trello card is moved into the list named Waiting for customer on a selected board.

Trello represents that as an updateCard action. A board webhook receives many board events, not only card moves, so your handler must inspect action.type and the action’s data before sending anything.

For a card move, the useful fields are normally under:

  • action.type — should be updateCard
  • action.id — the unique Trello action identifier
  • action.date — when Trello recorded the action
  • action.data.card — basic card information
  • action.data.listBefore — the list before the move
  • action.data.listAfter — the destination list
  • action.data.board — board metadata
  • action.memberCreator — the Trello member who performed the action, when present

The list transition matters more than the card’s current list alone. If the card is moved from Reviewing to Waiting for customer, that is different from a title edit performed while it was already in Waiting for customer. Checking listAfter.name lets you send only on the transition that matters.

A representative Trello card-move payload

A board webhook POST contains an action object and the watched model. The precise object can vary by action type and Trello may add fields over time, so your code should only depend on fields it needs.

{
  "action": {
    "id": "66fd0de7a96f57d1b3e4a111",
    "idMemberCreator": "64a19cc17ef6d8d32c8b1222",
    "type": "updateCard",
    "date": "2026-09-25T15:42:18.011Z",
    "data": {
      "card": {
        "id": "66fcf9d2b6210b9d7c3d3333",
        "name": "Acme onboarding checklist",
        "idShort": 42,
        "shortLink": "AbC123xY",
        "idList": "66fcef3ad1f7ce8bc9d94444"
      },
      "listBefore": {
        "id": "66fcedf1210df4bd7af95555",
        "name": "Reviewing"
      },
      "listAfter": {
        "id": "66fcef3ad1f7ce8bc9d94444",
        "name": "Waiting for customer"
      },
      "board": {
        "id": "66fceba2f98c8f7f24e76666",
        "name": "Customer onboarding",
        "shortLink": "QwErTy12"
      }
    },
    "memberCreator": {
      "id": "64a19cc17ef6d8d32c8b1222",
      "fullName": "Morgan Lee",
      "username": "morganlee"
    }
  },
  "model": {
    "id": "66fceba2f98c8f7f24e76666",
    "name": "Customer onboarding"
  }
}

Do not assume that every action includes every optional field. For example, an automation, a deleted member profile, a changed permission level, or a different Trello action can make memberCreator, listBefore, or other properties unavailable. Treat the webhook as external input: validate it before using it in a subject line, HTML body, recipient lookup, or database query.

Why the direct path needs middleware

Trello webhooks are available through Trello’s API. You create a webhook by associating a callback URL with a model such as a board, card, list, or member. Trello checks the callback URL with an HTTP HEAD request during webhook creation; the endpoint must return HTTP 200 or the webhook will not be created.

That detail rules out a tempting but unsafe design: calling Volanea directly from a Trello-visible client-side configuration. A Trello webhook needs a callback URL, and the endpoint behind it needs to hold the Volanea secret key. That endpoint is your integration boundary.

The architecture should look like this:

Trello board action
       |
       | HTTPS POST webhook payload
       v
Your webhook endpoint
  - answers HEAD 200
  - verifies Trello signature
  - filters the intended action
  - records an idempotency key
       |
       | POST /v1/send
       v
Volanea
       |
       v
Recipient inbox

This is a small amount of code, but it solves important operational problems. Your endpoint can reject an invalid webhook signature, avoid leaking the email key, ignore irrelevant card edits, and preserve the Trello action ID as the identity of the send.

If you do not maintain an application server, use a serverless function or worker with secure environment-variable support. The integration still needs a private runtime. A webhook relay or no-code middleware product can be appropriate when it provides secret storage, request filtering, logging, and reliable retries, but it does not eliminate the need to protect the Volanea key.

For Volanea request formats, authentication, templates, and sending options, use the email API reference and setup guides as the source of truth while implementing your endpoint.

Prerequisites before you send email from Trello

Set up the delivery foundation before creating the Trello webhook. Testing the webhook first and sender authentication later makes failures harder to diagnose because several independent systems are involved.

You need:

  • A Trello board that contains the workflow list you want to watch.
  • A Trello API application key and an access token with permission to read the watched board.
  • A public HTTPS endpoint you control, such as https://app.example.com/webhooks/trello.
  • The Trello application secret, used to validate webhook signatures.
  • A Volanea secret API key stored only in your runtime’s secret manager or environment variables.
  • A verified sending domain and a valid From address in that domain.
  • A stable alert recipient for the initial implementation, such as onboarding@example.com.
  • Persistent storage for processed Trello action IDs in production.

The verified sender is not optional. A message can be structurally valid JSON and still be rejected if its From domain is not authorized for your Volanea account. Start with an address such as operations@updates.example.com, not a personal mailbox, so the sender identity stays consistent as your team grows.

Create the Trello board webhook

Trello webhooks are created by API rather than by installing a native Trello integration. With a Trello API token, the nested token webhook endpoint can be used as follows:

curl --request POST \
  --url "https://api.trello.com/1/tokens/${TRELLO_TOKEN}/webhooks/?key=${TRELLO_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{
    "description": "Send an internal email when an onboarding card needs customer input",
    "callbackURL": "https://app.example.com/webhooks/trello",
    "idModel": "YOUR_TRELLO_BOARD_ID"
  }'

Use the board ID, not merely a board name. A name can be changed, duplicated, or localized; Trello IDs are the durable identifiers your integration needs. Before running this request, deploy the endpoint so its HEAD response returns HTTP 200.

A board webhook observes actions involving that board. Your application—not the Trello webhook registration—does the final filtering. This makes it easy to expand from one destination list to several rules later, but it also means you must avoid treating every updateCard event as an email event.

Working Node.js webhook-to-Volanea code

The example below uses Express and Node’s built-in crypto module. It handles Trello’s callback verification request, preserves the raw body for signature verification, checks that the event is a card move into Waiting for customer, and maps the Trello fields into a Volanea email request.

The recipient is intentionally an environment variable in this first version. A standard Trello card-move payload does not guarantee that a customer email address is present. Sending operational alerts to a controlled address is safer than guessing a recipient from a card title or description.

import express from "express";
import crypto from "node:crypto";

const app = express();

const CALLBACK_URL = "https://app.example.com/webhooks/trello";
const TARGET_LIST = "Waiting for customer";

// Keep all secrets server-side. Never put these in a Trello card, browser app,
// public repository, or client-visible automation setting.
const {
  TRELLO_APP_SECRET,
  VOLANEA_API_KEY,
  VOLANEA_FROM = "operations@updates.example.com",
  ALERT_TO = "onboarding@example.com"
} = process.env;

// Demo-only dedupe. Replace with a database table or Redis in production.
const processedActions = new Set();

function verifyTrelloSignature(rawBody, signature) {
  if (!signature || !TRELLO_APP_SECRET) return false;

  const expected = crypto
    .createHmac("sha1", TRELLO_APP_SECRET)
    .update(rawBody + CALLBACK_URL)
    .digest("base64");

  const actual = Buffer.from(signature, "utf8");
  const expectedBuffer = Buffer.from(expected, "utf8");

  return actual.length === expectedBuffer.length &&
    crypto.timingSafeEqual(actual, expectedBuffer);
}

async function sendWithVolanea({ action, card, board, listBefore, listAfter }) {
  const cardUrl = `https://trello.com/c/${card.shortLink}`;
  const movedBy = action.memberCreator?.fullName || "A Trello member";
  const fromList = listBefore?.name || "an earlier list";

  // Field mapping from Trello's webhook payload to Volanea's send payload:
  // action.id          -> Idempotency-Key
  // card.name          -> subject and email content
  // card.shortLink     -> Trello card URL
  // board.name         -> email content
  // listBefore.name    -> email content
  // listAfter.name     -> email content
  // memberCreator.name -> email content
  const emailPayload = {
    from: VOLANEA_FROM,
    to: ALERT_TO,
    subject: `Customer input needed: ${card.name}`,
    html: `
      <h2>Customer input needed</h2>
      <p><strong>${escapeHtml(card.name)}</strong> moved from
      <strong>${escapeHtml(fromList)}</strong> to
      <strong>${escapeHtml(listAfter.name)}</strong>.</p>
      <p>Board: ${escapeHtml(board.name)}<br>
      Moved by: ${escapeHtml(movedBy)}</p>
      <p><a href="${cardUrl}">Open Trello card #${card.idShort}</a></p>
    `,
    text: [
      "Customer input needed",
      `${card.name} moved from ${fromList} to ${listAfter.name}.`,
      `Board: ${board.name}`,
      `Moved by: ${movedBy}`,
      `Open card: ${cardUrl}`
    ].join("\n")
  };

  const response = await fetch("https://api.volanea.com/v1/send", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${VOLANEA_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": `trello-action-${action.id}`
    },
    body: JSON.stringify(emailPayload)
  });

  const result = await response.json().catch(() => ({}));

  if (!response.ok) {
    throw new Error(`Volanea send failed (${response.status}): ${JSON.stringify(result)}`);
  }

  return result;
}

function escapeHtml(value = "") {
  return String(value)
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#039;");
}

// Trello sends HEAD when validating callbackURL during webhook creation.
app.head("/webhooks/trello", (_req, res) => res.sendStatus(200));

app.post("/webhooks/trello", express.raw({ type: "application/json" }), async (req, res) => {
  const rawBody = req.body.toString("utf8");
  const signature = req.get("x-trello-webhook");

  if (!verifyTrelloSignature(rawBody, signature)) {
    return res.sendStatus(401);
  }

  const payload = JSON.parse(rawBody);
  const { action } = payload;
  const data = action?.data || {};
  const { card, board, listBefore, listAfter } = data;

  const isTargetMove =
    action?.type === "updateCard" &&
    card?.id &&
    board?.id &&
    listAfter?.name === TARGET_LIST &&
    listBefore?.id !== listAfter?.id;

  // Acknowledge irrelevant, valid webhook events without sending an email.
  if (!isTargetMove) {
    return res.sendStatus(200);
  }

  // Do not process the same Trello action more than once.
  if (processedActions.has(action.id)) {
    return res.sendStatus(200);
  }

  try {
    await sendWithVolanea({ action, card, board, listBefore, listAfter });
    processedActions.add(action.id);
    return res.sendStatus(200);
  } catch (error) {
    console.error("Trello-to-Volanea send failed", {
      actionId: action.id,
      message: error.message
    });

    // Return a failure only when you have durable idempotency and a retry plan.
    // Trello may redeliver this event, so the Volanea idempotency key is essential.
    return res.sendStatus(500);
  }
});

app.listen(3000, () => {
  console.log("Listening on http://localhost:3000");
});

The code uses the Trello action ID in the Idempotency-Key header. That is the correct logical identity for this email: one Trello action should result in one send attempt, regardless of whether Trello or your service retries delivery.

For a production deployment, replace the in-memory Set with durable storage. A simple table keyed by trello_action_id is enough. Insert the action ID in a transaction before dispatching the email, retain the Volanea response ID and status, and make a repeated insert harmless. The API-level idempotency key is a second line of defense, not a replacement for your own event ledger.

Keep the Volanea API key out of Trello

The Volanea API key belongs in the environment of your webhook service. It should never be stored in:

  • A Trello card description or comment
  • A custom field on a Trello card
  • A card attachment
  • Client-side JavaScript
  • A public Git repository
  • A URL query parameter
  • A browser-accessible configuration endpoint
  • An unprotected Zapier, Make, or workflow variable shared broadly with editors

An API key can send email on behalf of your configured account. Exposing it to people who can view a board, inspect a browser network request, or copy an automation configuration can turn a small notification workflow into an unauthorized sending risk.

Use your hosting provider’s secret manager, encrypted environment variables, or a managed runtime secret store. Name the variable clearly, such as VOLANEA_API_KEY, and keep separate keys for development, staging, and production if your account setup supports that separation.

Trello’s own application secret is also sensitive. Your handler uses it to verify the x-trello-webhook signature. The verification computes an HMAC-SHA1 over the exact raw request body concatenated with your callback URL, then compares that value to Trello’s header. Parsing JSON before saving the raw bytes can break verification because whitespace and serialization details may differ.

The callback URL is part of the signed input. If you deploy behind a proxy, use the exact URL registered with Trello, including the scheme, host, path, and any query string. Do not construct it from an untrusted Host header at request time.

Choosing recipients and email content safely

The code sends to one internal recipient because it is a reliable starting point. If you want to send to a customer or card owner, create an explicit source of recipient truth rather than deriving an address from unstructured card text.

Safer recipient patterns

A strong implementation uses one of these patterns:

  1. Lookup by card ID. Your application creates the Trello card and stores a mapping of card.id to a customer record and approved email address.
  2. Lookup by external case ID. Put a stable case ID in the card title or a custom field, then resolve it in your database.
  3. Controlled internal routing. Map destination list IDs to internal addresses such as support, legal, finance, or operations.
  4. Approved custom-field workflow. Fetch the current card and custom field values through the Trello API after the webhook arrives, then validate the address against your business rules.

Avoid treating an arbitrary email-like string in a Trello description as permission to send. Card content can be copied, outdated, publicly shared, or edited by someone without authority to trigger customer communication.

Also avoid writing raw Trello text into HTML without escaping it. Card titles and comments are user-generated content. In the example, escapeHtml() protects the HTML body. It is still worth setting practical limits on subject length and stripping unexpected control characters before creating message headers.

Prefer list IDs to list names in mature workflows

The example checks listAfter.name because it is readable. In production, use the list ID once your rule is established. Team members often rename lists from Waiting for customer to Awaiting reply, translate them, or temporarily change labels during process redesign.

A robust rule combines both during rollout:

const TARGET_LIST_ID = "66fcef3ad1f7ce8bc9d94444";
const isTargetMove =
  action?.type === "updateCard" &&
  listAfter?.id === TARGET_LIST_ID &&
  listBefore?.id !== listAfter?.id;

You can retain the human-readable list name in the email, but the ID should drive the automation. Record the board ID too, even if a webhook is already scoped to that board, so a copied endpoint cannot accidentally process events from a different integration.

When this breaks: Trello-to-email failure modes

A Trello-to-Volanea integration crosses multiple systems. Most failures are understandable once you identify which hop failed: Trello did not create or deliver the webhook, your endpoint rejected or timed out, your application filtered the event, or Volanea declined the email request.

Trello cannot create the webhook

Trello validates a callback URL with an HTTP HEAD request during webhook creation. If your endpoint only implements POST, returns a redirect, requires a browser login, or returns a non-200 response to HEAD, registration fails.

Fix this by exposing an unauthenticated HEAD /webhooks/trello route that returns 200. It should not send email, create records, or require a session cookie. The endpoint still verifies signatures for actual POST deliveries.

Webhook timeouts create ambiguous outcomes

A webhook request can time out or encounter a transient network failure after your application has already called Volanea. That creates the classic ambiguity: Trello may retry even though the email was accepted, and your own handler may be invoked again.

Use the Trello action.id as the durable event ID and use the same value-derived Volanea Idempotency-Key for every retry of that action. Never generate a random idempotency key inside each retry attempt; doing that would make each retry look like a different email.

For high-volume workflows, acknowledge a valid webhook quickly after writing the event into a durable queue or database outbox. A worker can then call Volanea separately. This reduces timeout risk, isolates email-provider latency from Trello’s delivery window, and makes retries visible in your own logs.

Trello retries cause duplicate sends

Webhook delivery should be treated as at-least-once, not exactly-once. Your endpoint can receive the same Trello action more than once due to retry behavior, deployment interruptions, or an ambiguous response.

The fix has three layers:

  • Verify the Trello signature before accepting the payload.
  • Store action.id in a durable processed-events table with a unique constraint.
  • Send the same Idempotency-Key to Volanea for every attempt tied to that action.

If the first email request succeeds but your server crashes before marking the event complete, the repeated request should reuse the same idempotency key. If the first request never reached Volanea, the retry can safely create the one intended email.

Payload fields are missing or differ by action

A board webhook can receive comment events, archive events, member changes, list changes, and card changes. They do not all have listBefore, listAfter, card.shortLink, or memberCreator in the same shape.

Do not write code that assumes every payload has every property. Guard each condition, log the action type and action ID for ignored events, and only create an email when the required fields are present. If a workflow needs richer current card data—such as custom field values, members, labels, or descriptions—retrieve that data from Trello’s API after verifying the webhook instead of assuming it is embedded in the event.

Some feature availability and field behavior can also vary with Trello product configuration, permissions, and the type of model watched. Your integration should fail closed: if the recipient source or required workflow field is missing, do not send a partially addressed or misrouted email. Record the event for operator review instead.

Volanea rejects the send

A Volanea error is usually actionable. Common causes include a missing or invalid API key, an unverified From domain, malformed recipient data, account limits, or a suppressed recipient. Log the HTTP status and the response body without logging the API key or unnecessary personal data.

Do not interpret an accepted API request as proof that the message reached the inbox. Acceptance means Volanea has accepted the sending request; delivery can still be affected by recipient suppression, mailbox rejection, bounces, and downstream provider behavior. Use email events and send logs to investigate delivery after acceptance.

Testing the integration before production

A reliable test plan exercises each boundary separately. Do not make a real card move the first time you discover that a webhook URL, a sender domain, and an API key all need configuration.

Test in this order

  1. Test the callback URL. Deploy the endpoint and confirm HEAD /webhooks/trello returns HTTP 200.
  2. Create the Trello webhook. Confirm the API returns an active webhook object.
  3. Test signature rejection. POST an unsigned request and confirm the endpoint returns 401 without sending email.
  4. Test a valid fixture. Use a saved card-move payload and a valid calculated signature in a non-production environment.
  5. Move a test card. Move it from a non-target list into the target list and inspect logs for the Trello action ID.
  6. Confirm the Volanea request. Check that the From address, recipient, subject, card URL, and idempotency key are correct.
  7. Replay the same event. Confirm your event ledger and Volanea idempotency behavior prevent a second message.
  8. Test a non-target update. Rename a card inside the target list. It should not send an email because no list transition occurred.

Use a dedicated test board and a mailbox you control. That avoids accidental customer emails while your rules are still evolving. It also lets you test moved-back-and-forward behavior: decide whether a card moved from Waiting for customer to Reviewing and back again should send a new email. If yes, the different Trello action IDs naturally permit distinct notifications. If not, add a business-level rule such as one notification per card per day or per workflow state.

Extending the integration beyond one alert

Once the webhook path is working, you can turn it into a broader email workflow without changing the underlying architecture.

For example, route by destination list:

  • Waiting for customer → notify customer-success team
  • Legal review → notify legal operations
  • Ready to send → notify a campaign owner
  • Escalated → notify an incident-response mailbox

Or route by board:

  • Sales board → send internal deal-stage notifications
  • Recruiting board → send interview coordination alerts
  • Customer onboarding board → send handoff reminders
  • Editorial board → send publishing approval requests

For messages that need a consistent design, move the subject and content into a Volanea template and send variables such as cardName, boardName, movedBy, fromList, toList, and cardUrl. This keeps branding and copy changes out of deployment code while retaining the same secure event handling.

If your volume grows, use an outbox pattern. The webhook handler stores a normalized event record and returns promptly. A worker processes that record, calls Volanea, records the result, and retries controlled failures with the same idempotency key. This separates Trello’s inbound delivery expectations from your outbound email workload.

It is also worth planning the economics of frequent workflow notifications. Review transactional email pricing before expanding a card-by-card alert into a high-volume operational workflow, especially if list changes are frequent or board activity can trigger messages in bursts.

Conclusion

The dependable way to send email from Trello with Volanea is not a native plugin install. It is a small, explicit integration: register a Trello board webhook, validate its signed payload, identify the updateCard move that matters, and make a server-side POST /v1/send call with a stable idempotency key.

That approach protects the Volanea API key, keeps recipient decisions under your control, and prevents a Trello retry from turning into duplicate email. Start with one board, one list transition, and one internal recipient. Then add recipient lookups, templates, durable queues, and event monitoring as the workflow becomes business-critical.

FAQ

Can Trello send an email directly through Volanea?

Not through a native Volanea Trello app or Marketplace plugin. Use a Trello webhook that posts to a server-side endpoint, then have that endpoint call Volanea’s REST API with the API key stored as a secret.

What Trello action triggers the email in this guide?

The trigger is an updateCard action where a card is moved into the Waiting for customer list. The handler checks action.data.listAfter and confirms the source and destination list IDs differ.

Where should I store the Volanea API key?

Store it in your server, serverless function, or worker secret manager as an environment variable such as VOLANEA_API_KEY. Do not store it in Trello cards, browser code, public URLs, or shared visible configuration.

How do I stop duplicate emails when Trello retries a webhook?

Use Trello’s action.id as your event ID, save it in durable storage with a unique constraint, and use a deterministic Volanea Idempotency-Key derived from that action ID for every retry.

Can I email the person associated with a Trello card?

Yes, but use an explicit recipient lookup. Map the Trello card ID or an external case ID to an approved customer record in your database, or fetch controlled custom-field data after verifying the webhook. Do not infer recipient authorization from free-form card text.