Teamleader can be the event source for customer communication, while Volanea handles delivery. This guide shows how to send email from Teamleader without pretending there is a native Teamleader marketplace app: Teamleader emits an event, your server enriches it through the Teamleader API, and that server calls Volanea’s email API.

The important architectural detail is that a Teamleader webhook is a notification, not a complete mail-merge record. Treat it as a prompt to fetch the current contact data, apply your own sending rules, and create one deliberate transactional or campaign email request.

What this integration does—and what it does not do

Volanea does not provide a native Teamleader app, marketplace listing, or in-product plugin. You cannot install Volanea inside Teamleader and have it automatically access every contact, deal, or invoice.

Instead, the supported pattern is a server-to-server integration built around Teamleader’s API and webhook events. The flow is:

  1. A Teamleader event occurs, such as the contact.added webhook event.
  2. Teamleader POSTs a small event notification to an HTTPS endpoint you control.
  3. Your endpoint checks that the event is relevant and stores it durably.
  4. A worker uses the webhook subject ID to request the contact from Teamleader’s API.
  5. The worker validates the address, chooses a template or message, and sends through Volanea.
  6. Your application records the Teamleader subject ID and Volanea message identifier so the event is auditable and idempotent.

This separation is useful even for a straightforward welcome email. It keeps credentials off the browser, gives you a place to suppress duplicates, and lets you decide whether a newly created contact actually has permission to receive a particular type of email.

A contact-created event is a good example because it has a clear business meaning. For other workflows, use the Teamleader webhook event that matches the moment you want to communicate—rather than trying to infer changes by repeatedly polling Teamleader.

The concrete Teamleader trigger: contact.added

For this implementation, the trigger is Teamleader’s contact.added webhook event. It occurs when a contact is added in Teamleader. The event name matters: it is an event notification, not an instruction to send a marketing email to every address that enters the CRM.

Before enabling an automated send, define the actual business rule around it. A contact may have been added by a sales representative, imported in bulk, created by an integration, or entered after a customer completed a form elsewhere. Those sources can have very different consent and expectation contexts.

A defensible initial rule might be: send a transactional welcome or account-next-steps message only when the contact was created by an opted-in signup flow, and otherwise merely synchronize the contact for later use. If the intended message is promotional, keep a consent field and suppression logic in your own integration or approved marketing system; contact creation alone is not proof of marketing consent.

What Teamleader posts to your endpoint

Teamleader webhook messages identify the event and its subject. The key point is that the notification identifies the contact; it should not be assumed to include the full current contact profile or an email address suitable for immediate delivery.

A contact.added notification has this shape:

{
  "event": "contact.added",
  "subject": {
    "type": "contact",
    "id": "0f9db4d0-7d3d-4b49-9d3c-2d5eb26cb414"
  },
  "timestamp": "2025-03-08T12:34:56+00:00"
}

Your code should use subject.id as the stable reference for the next API call. Do not map an email recipient from the webhook notification itself, because it is not a complete contact object. That extra lookup also avoids sending to stale data if a contact has been edited between event emission and processing.

Subscribe from an integration, not from browser code

Webhook subscriptions are part of a Teamleader API integration. A Teamleader OAuth access token and the API scopes required by your integration belong in a server-side service. The callback URL must be publicly reachable over HTTPS, and it should point to a route you own, such as https://integrations.example.com/teamleader/webhooks.

Keep the webhook endpoint narrow. It should receive events, make a fast acceptance decision, enqueue them, and return success. It should not wait for a contact lookup, a template render, and an email-provider request in the same HTTP request if you can avoid it.

Why a middleware service is required

Teamleader can notify an external HTTPS endpoint, but it is not a secure place to hold an unrelated email-provider secret. In particular, a webhook URL or a client-side automation configuration is not an acceptable storage location for a Volanea API key.

Your middleware can be a small Node.js service, a serverless function plus queue, or an existing backend. It has three jobs: receive Teamleader events, call Teamleader for the authoritative record, and call Volanea for delivery.

This also gives you a control point that Teamleader alone cannot provide:

  • Idempotency: prevent retries from creating multiple welcome emails.
  • Eligibility: require a valid email address and the right consent or lifecycle state.
  • Observability: retain the source event, contact ID, request outcome, and provider response.
  • Failure handling: retry transient API failures without making the webhook sender wait.
  • Content control: use a versioned template and a predictable fallback when optional fields are absent.

For a low-volume proof of concept, the receiver and worker can be one application process. For production, acknowledge the webhook after persisting it, then process a queue in the background. A queue makes both Teamleader delivery and Volanea delivery less fragile during temporary outages.

Authentication and where the Volanea key belongs

The Volanea API key lives nowhere on the Teamleader side. It belongs in the secret store or encrypted environment configuration of your middleware service—for example, a deployment-platform secret named VOLANEA_API_KEY.

Teamleader needs only the webhook callback URL and the OAuth authorization relationship needed by the integration to read contact details. Your service uses a separate Teamleader access token to call Teamleader’s API and a separate Volanea API key to send mail. Do not combine these credentials or expose either one in frontend JavaScript.

A safe environment configuration looks like this:

TEAMLEADER_ACCESS_TOKEN=server-side-oauth-access-token
VOLANEA_API_KEY=server-side-volanea-api-key
VOLANEA_FROM=Customer Team <hello@example.com>
TEAMLEADER_WEBHOOK_SECRET=your-own-random-endpoint-secret

The webhook-secret example deserves an explanation. If your receiving route needs an additional shared secret or other request-authentication mechanism, implement that in your own endpoint design and validate it before queueing work. Do not assume that an unverified custom header exists or that a URL is secret simply because it is hard to guess.

Rotate Volanea keys when staff or systems change, restrict access to production secrets, and make sure application logs never print the Authorization header. Email recipients, contact IDs, and rendered bodies can also be personal data, so log only what your support and audit process genuinely needs.

For endpoint details, request fields, and current authentication examples, use the Volanea email API reference rather than copying assumptions from another provider’s API.

Fetch the Teamleader contact before composing mail

After receiving contact.added, request the contact with Teamleader’s contacts.info endpoint. The request body uses the webhook subject ID:

POST https://api.focus.teamleader.eu/contacts.info
Authorization: Bearer YOUR_TEAMLEADER_ACCESS_TOKEN
Content-Type: application/json

{
  "id": "0f9db4d0-7d3d-4b49-9d3c-2d5eb26cb414"
}

The contact response includes the contact’s identity and its email collection. A contact can have no email address, multiple email addresses, or a primary address that is not appropriate for the email you intend to send. Your integration must explicitly select one rather than blindly treating the first returned value as a recipient.

A practical selection policy is to prefer the item designated as primary, normalize whitespace, reject empty values, and then apply your own suppression and consent tests. If Teamleader contact data is not complete on every subscription or in every workflow, make missing data an expected branch, not an exception that causes repeated sends.

Field mapping for a welcome-email workflow

The following mapping is intentionally small and explainable:

Teamleader sourceMiddleware fieldVolanea email field
subject.id from webhookteamleaderContactIdstored as metadata/reference
contact first_namefirstNameused in HTML/text content
contact last_namelastNameoptional personalization context
primary item in contact emailsrecipientto
configured server secretfromfrom
workflow rulesubjectsubject

Notice that sender identity is configuration, not a Teamleader contact field. Use a verified sending domain and a sender mailbox that matches the purpose of the message. A welcome workflow should not impersonate the sales representative who happened to create the CRM contact unless that is truly your approved communication design.

Working Node.js webhook-to-email example

The example below receives a Teamleader contact.added payload, fetches the contact, chooses a primary address, and makes a Volanea REST email request. It uses a simple in-memory idempotency set only to make the logic visible; replace it with a database table or queue-backed unique key in production.

import express from "express";

const app = express();
app.use(express.json({ type: "application/json" }));

const processed = new Set(); // Production: unique DB key, not memory.

function primaryEmail(contact) {
  const emails = Array.isArray(contact.emails) ? contact.emails : [];
  const primary = emails.find((item) => item.type === "primary") || emails[0];
  const value = primary && typeof primary.email === "string"
    ? primary.email.trim()
    : "";

  // Keep deeper validation/suppression checks in your production service.
  return value.includes("@") ? value : null;
}

app.post("/teamleader/webhooks", async (req, res) => {
  const event = req.body;

  // Accept only the event and subject type used by this workflow.
  if (event?.event !== "contact.added" || event?.subject?.type !== "contact") {
    return res.sendStatus(204);
  }

  const contactId = event.subject.id;
  const idempotencyKey = `${event.event}:${contactId}`;

  // A durable version of this check must happen before sending.
  if (processed.has(idempotencyKey)) return res.sendStatus(204);

  try {
    const contactResponse = await fetch(
      "https://api.focus.teamleader.eu/contacts.info",
      {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${process.env.TEAMLEADER_ACCESS_TOKEN}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ id: contactId })
      }
    );

    if (!contactResponse.ok) {
      throw new Error(`Teamleader contact lookup failed: ${contactResponse.status}`);
    }

    const { data: contact } = await contactResponse.json();
    const recipient = primaryEmail(contact);

    // No address means no email; record this outcome in a real datastore.
    if (!recipient) return res.sendStatus(204);

    const firstName = String(contact.first_name || "there").replace(/[<>&]/g, "");
    const html = `<p>Hi ${firstName},</p><p>Thanks for getting in touch. We’ll be in touch shortly.</p>`;
    const text = `Hi ${firstName},\n\nThanks for getting in touch. We’ll be in touch shortly.`;

    const emailResponse = await fetch("https://api.volanea.com/v1/emails", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.VOLANEA_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey
      },
      body: JSON.stringify({
        from: process.env.VOLANEA_FROM,
        to: [recipient],
        subject: "Thanks for getting in touch",
        html,
        text,
        metadata: {
          source: "teamleader",
          teamleader_contact_id: contactId,
          teamleader_event: event.event
        }
      })
    });

    if (!emailResponse.ok) {
      throw new Error(`Volanea send failed: ${emailResponse.status}`);
    }

    processed.add(idempotencyKey);
    return res.sendStatus(202);
  } catch (error) {
    console.error(error.message);
    // Return a non-2xx response only if your durable retry design expects it.
    return res.sendStatus(500);
  }
});

app.listen(3000);

The field mapping is in the code, not hidden behind a vague “send contact to Volanea” step: contact.emails becomes to, contact.first_name becomes personalized content, a configured sender becomes from, and subject.id is retained as source metadata and an idempotency input.

Before deploying, check the current Volanea request schema in the documentation and adapt only the provider-specific request details if necessary. Keep the workflow structure unchanged: event, durable record, lookup, eligibility, send, and outcome record.

Design the email for deliverability and customer expectations

An API integration can successfully return a send response and still create poor inbox outcomes if the content or list logic is wrong. Start with a verified sender domain, publish the DNS authentication records required by your sending setup, and align the visible From identity with the domain that sends the message.

For a contact-created workflow, keep the first message useful and narrow. Explain why the person is receiving it, avoid unnecessary tracking or heavy promotional language, and include a plain-text alternative. If you need to verify whether an address is usable before adding it to an operational workflow, the email address verification tool can help with individual-address checks; it does not replace consent management or your own suppression list.

Avoid placing sensitive CRM details in the message body. A Teamleader contact can be associated with commercially sensitive work, and email is not a safe transport for account information that should remain behind authenticated access. Prefer a signed or authenticated link to your application when the recipient must view private data.

When this breaks: failures specific to the Teamleader hop

The weakest part of this flow is often not the email API call. It is the boundary between Teamleader’s event delivery and your application’s processing. Build for these cases from the beginning.

Teamleader retries can produce duplicate sends

Webhook delivery is not a guarantee of exactly-once processing. If your endpoint times out, returns an error after Volanea has accepted the email, or loses its response connection, Teamleader may retry the event. If every receipt immediately sends an email, one contact can receive duplicates.

Use a durable idempotency key. For this simple event, contact.added:{contactId} is usually appropriate because the business action is “one welcome email per newly created contact.” Store that key with a unique constraint before sending, then store the Volanea response alongside it. If the workflow is intentionally repeatable, include a deliberate campaign or lifecycle version in the key, such as welcome-v2:{contactId}.

Do not depend on an in-memory Set as shown in the sample. It disappears on restart and does not coordinate between serverless instances. A database unique index, queue deduplication feature, or transactional outbox is the production answer.

Webhook timeouts create uncertainty

A webhook receiver that performs several outbound calls before replying can exceed the sender’s delivery timeout. Even if both external calls eventually succeed, Teamleader may regard the delivery as failed and retry.

Respond quickly after validating and persisting the event. Then let a worker perform contacts.info and the Volanea request. If your platform does not have a queue, create a table with statuses such as received, processing, sent, skipped_no_email, and failed, and have a scheduled worker claim rows safely.

Payload fields can be absent or insufficient

The webhook payload identifies the subject, but it is not a full contact snapshot. More importantly, a Teamleader contact may not have an email address, may have more than one, or may not include the custom data your organization wants to use for segmentation. Feature availability and data fields can also differ according to Teamleader configuration, user permissions, and subscription.

Make optional fields optional in code. Use fallback copy when first_name is absent, skip rather than guess when no recipient exists, and record a clear reason. If your workflow depends on a custom field or deal state, verify that the API token can read it and test with accounts that reflect the plans and configurations your customers actually use.

Access tokens and permissions expire or change

Your Teamleader integration needs valid OAuth authorization and sufficient access to retrieve the contact triggered by the event. A revoked authorization, expired token, changed user permission, or missing scope can turn a previously working lookup into a 401 or 403 response.

Treat authorization failures differently from temporary network failures. Alert an administrator, stop automatic retries after a bounded number of attempts, and do not mark an event as successfully emailed when the lookup was never completed. Build a reconnection path so the account owner can restore authorization without engineering intervention.

Testing the workflow before enabling it

Test with a non-production sender domain or a controlled test list first. Create a Teamleader contact with a primary address, a contact with no address, a contact with multiple addresses, and a contact without a first name. Verify the outcome in your middleware records and in Volanea’s sending activity.

A useful acceptance checklist is:

  • The receiver ignores events other than contact.added.
  • A valid contact results in one correctly addressed message.
  • Replaying the same webhook does not create a second message.
  • A missing address produces a recorded skip, not an exception loop.
  • A Teamleader API authorization failure creates an actionable alert.
  • A Volanea temporary failure retries through the worker without reprocessing the source event incorrectly.
  • Secrets are absent from source control, browser bundles, request URLs, and application logs.

Also test your unsubscribe and suppression behavior where applicable. A transactional message may have a different legal and product purpose from a promotional campaign, but neither category justifies repeatedly contacting an address that has been suppressed for a valid reason.

Alternatives when you do not want to run code

If your team does not operate a backend, use an automation platform as the middleware layer only if it can support the exact Teamleader trigger, a secure connection, a Teamleader contact lookup, and an authenticated HTTP request to Volanea. The automation should still follow the same logical sequence: event, contact lookup, recipient selection, eligibility checks, and send.

Be cautious with no-code designs that place a long-lived API key in a broadly shared scenario or expose it to many editors. Limit who can edit the automation, use its encrypted connection or secret facilities where available, and make duplicate prevention explicit. A visual workflow does not remove the need for idempotency; webhook retries are still retries.

For high-value or high-volume mail, a small backend is usually easier to audit than a complex chain of automation steps. It also provides better control over consent, templates, provider responses, retries, and data retention.

A production-ready architecture

A robust version of this integration has four components: the webhook receiver, a durable event store, a worker, and an email audit record. The receiver should do as little as possible beyond validation and persistence. The worker should claim one event, fetch Teamleader data, evaluate policy, submit the Volanea request with an idempotency key, and commit the final status.

Store enough information to answer support questions later: the Teamleader event name, subject type and ID, receipt time, selected recipient in protected form where appropriate, template version, idempotency key, Volanea message ID, and failure class. Avoid retaining the entire raw contact object forever unless there is a clear operational and privacy reason.

This architecture also makes future changes safer. You can add a second message after a deal milestone, change welcome copy, or route different Teamleader organizations through distinct sender domains without rewriting the webhook endpoint. Each new rule becomes an explicit worker policy rather than an accidental side effect of CRM activity.

Conclusion

To send email from Teamleader with Volanea, use Teamleader’s contact.added webhook as the starting signal, not as a complete contact record. Your server should fetch the contact through contacts.info, select and validate the recipient, and send a deliberately composed email through Volanea using a server-side API key.

The result is more reliable than a direct client-side connection: credentials stay protected, missing data is handled intentionally, retries cannot casually duplicate messages, and every send has a traceable Teamleader source. Start with one narrow lifecycle message, make it idempotent, and expand only after the event and consent rules are proven.

FAQ

Does Teamleader have a native Volanea integration?

No. This workflow uses Teamleader webhook events plus your own middleware service and Volanea’s REST API. There is no native Teamleader marketplace installation flow for Volanea.

Can I send the email directly from the webhook payload?

You should not. The contact.added webhook identifies a contact through subject.type and subject.id; fetch the contact through Teamleader’s API to obtain the current email address and personalization fields.

Where should I store the Volanea API key?

Store it only in a server-side secret manager or encrypted environment variable for your middleware. Do not place it in Teamleader client-visible settings, a webhook URL, frontend code, or a shared spreadsheet.

How do I stop duplicate emails after a retry?

Create a durable unique idempotency key before sending, such as contact.added:{contactId}, and retain the send result. Replayed webhook deliveries should find the existing key and exit without sending again.

What happens if the new Teamleader contact has no email address?

Mark the event as skipped with a clear reason. Do not invent an address, and do not retry indefinitely unless a separate business process later updates the contact and deliberately triggers an eligible workflow.