INTEGRATIONS · Published 2026-10-06 · Updated 2026-10-06 · SecureSMTP

Express

A contact endpoint that sends through SecureSMTP.

A POST /contact endpoint that checks the input and sends it to you as an email. It works with Express 4 and 5.

Setup

Every request needs your site’s API key. Get it when you add the site under Sites (/app/forms/sites) in the dashboard — see Sites and API keys. Keep it in an environment variable on the server:

Shell
export SECURESSMTP_API_KEY="qcs_live_..."
Never put the key in browser JavaScript, a mobile app or a public repository. Anyone who has it can send email as your site.

Copy securessmtp.js from the Node.js guide next to your server file. It has the sendEmail function used below.

The contact endpoint

server.js
// server.js
import express from 'express';
import { sendEmail } from './securessmtp.js';

const app = express();
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({ extended: false, limit: '100kb' }));

const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function validate(body) {
  const name = String(body?.name ?? '').trim();
  const email = String(body?.email ?? '').trim();
  const message = String(body?.message ?? '').trim();
  const errors = {};

  if (!name || name.length > 100) errors.name = 'Enter your name.';
  if (!EMAIL_RE.test(email) || email.length > 254) errors.email = 'Enter a valid email address.';
  if (!message || message.length > 5000) errors.message = 'Enter a message of up to 5,000 characters.';

  return { name, email, message, errors };
}

app.post('/contact', async (req, res) => {
  const { name, email, message, errors } = validate(req.body);
  if (Object.keys(errors).length > 0) {
    return res.status(422).json({ ok: false, errors });
  }

  try {
    await sendEmail({
      to: process.env.CONTACT_TO, // where contact messages should go
      subject: `Contact form: ${name}`,
      text: `Name: ${name}\nEmail: ${email}\n\n${message}`,
      from: { name: 'Website contact form' },
      reply_to: email,
      source_plugin: 'express-contact',
    });
    return res.json({ ok: true });
  } catch (err) {
    console.error('Contact email failed:', err.message);
    return res.status(502).json({ ok: false, error: 'Could not send your message. Please try again later.' });
  }
});

app.listen(3000, () => console.log('Listening on http://localhost:3000'));
  • reply_to is the visitor’s address, so you can answer with Reply in your email program.
  • source_plugin is a label. It shows in the email log so you can tell these emails apart.
  • The endpoint accepts both JSON and normal form posts. The input is limited before it is sent, so a visitor cannot send you a huge message.
This endpoint has no spam protection of its own. Add a rate limit per IP and a captcha, or use the Forms API, which has both. A contact-form notification that SecureSMTP judges to be spam is held: the API answers "ok": true, "held": true and the email is not sent.

Handle errors

sendEmail throws when the API does not answer "ok": true. The endpoint above logs the reason and returns 502 to the visitor.

ResponseMeaningWhat to do
200 ok: trueAccepted for sending.Nothing. Keep message_id if you want to find the email later.
200 rate_limitedMore than 120 sends from this site in 60 seconds.Wait a minute, then try again.
200 over_quotaThe account’s monthly email count is used up (relay mode only).Wait for the new month, upgrade, or send from a verified domain.
200 send_failedThe email was not sent. The error field says why.Read error. Do not retry in a loop.
400 invalid_payload, no_body, …The request is wrong. details lists the fields for invalid_payload.Fix the request. Retrying the same request fails the same way.
400 content_flagged, ai_flaggedThe content was judged to be spam.Change the content. See Deliverability.
401 missing_api_key, invalid_api_keyNo key was sent, or the key is wrong or was rotated.Check the environment variable on the server.
403 site_disabledThe site is disabled.See Blocks.
  • Check ok in the body, not only the HTTP status. Rate limit, monthly limit and send failures come back with HTTP 200.
  • There is no idempotency key. If a request times out, the email may already have been sent, and sending it again can deliver it twice.
  • Every reason is listed in the error catalogue.

Or use SMTP

To send with Nodemailer over SMTP instead of the API, see the Node.js guide.

Next