Skip to content

Docs

Set up Ascue

Three ways in. Email forwarding works with any form and takes about two minutes. The WordPress plugin and the API are there when you want more control.

Getting started

  1. Create an account with your email address and a password, then confirm your address from the link we email you. After that, sign in with your password, or ask for a one-time sign-in link by email instead.
  2. Add a site: its name, website, one sentence on what the business does, where leads should go, and how strict Ascue should be.
  3. Copy your site's forwarding address (it looks like k7f3q9@in.ascue.dev) and, if you'll use the plugin or API, its site key. The full key is shown only once, when the site is created. You can rotate it later in the site's settings.
  4. Connect your form using one of the three ways below.
  5. Send a test message through your form. It shows up in your dashboard within seconds, with its label.

Email forwarding

Your form already emails you when someone fills it in. Point that notification email at your site's private Ascue address instead of your own. Ascue asks each message its questions, then forwards everything to the address you chose for leads, except the spam and pitches it holds back.

  • Leads arrive from ascue.dev with the visitor as Reply-To, so hitting reply goes to them. Urgent ones start with "Urgent:".
  • Ask me messages arrive marked "Ascue wasn't sure", with one-click buttons to confirm or correct the label.
  • Held messages wait in your dashboard for 30 days, and you can release any of them.
  • If Ascue is down, your mail server holds the forwarded email and delivers it when Ascue is back. It arrives late, not lost.

Replace your own address rather than adding Ascue next to it, or you'll get every message twice.

WPForms

  1. In WordPress, open WPForms and edit your form.
  2. Go to Settings, then Notifications.
  3. Replace the address in Send To Email Address with your Ascue address.
  4. Set Reply-To to the form's email field, so replying to a forwarded lead goes to the visitor.
  5. Save, then send a test message through the form.

Contact Form 7

  1. In WordPress, go to Contact, then Contact Forms, and edit your form.
  2. Open the Mail tab.
  3. Replace the address in the To field with your Ascue address.
  4. Keep the Reply-To line in Additional headers (Reply-To: [your-email]), so replies go to the visitor.
  5. Save, then send a test message through the form.

Webflow

  1. Open Site settings, then the Forms tab.
  2. Under form notifications, add your Ascue address as a Send to address and remove your own.
  3. If your form has its own notification settings, update those too.
  4. Publish the site, then send a test message through the form.

Custom notification addresses may need a paid Webflow site plan. Webflow can also send a form submission webhook straight to Ascue: see Form webhooks in the docs.

Squarespace

  1. Edit the page and open the form block.
  2. Go to the Storage settings and choose Email.
  3. Enter your Ascue address and remove your own.
  4. Save, then send a test message through the form.

Wix

  1. Wix sends form notifications to the site's owners, so use an automation instead.
  2. In the dashboard, open Automations and create a new one.
  3. Choose the trigger for your form being submitted, and the action that sends an email.
  4. Send it to your Ascue address, including the form's fields in the email.
  5. Turn off your own form notification so messages only reach you through Ascue.

Wix changes its editor often, so the names of these menus may differ. An automation can also call a webhook instead of sending an email: see Form webhooks in the docs.

Google Forms

  1. Google Forms' own notifications say a response arrived but don't include it, so a short script sends each response instead.
  2. Open the form, then the More menu (three dots), then Apps Script.
  3. Paste the script below with your Ascue address, and save.
  4. Open Triggers (the clock icon), add a trigger for onFormSubmit, from form, on form submit, and allow the permissions it asks for.
  5. Submit a test response.
Google Apps Script
// Your site's Ascue forwarding address, from the dashboard.
const ASCUE_ADDRESS = 'your-address@in.ascue.dev';

function onFormSubmit(e) {
  const answers = e.response
    .getItemResponses()
    .map((r) => r.getItem().getTitle() + ': ' + r.getResponse());

  const message = {
    to: ASCUE_ADDRESS,
    subject: 'New response: ' + e.source.getTitle(),
    body: answers.join('\n\n')
  };
  // Only there if the form collects email addresses.
  const email = e.response.getRespondentEmail();
  if (email) message.replyTo = email;

  MailApp.sendEmail(message);
}

Any other form

If your form builder lets you type the address its notifications go to, it works with Ascue: put your Ascue address there. If it can only notify the account owner, look for an automation or integration that sends an email, a form webhook, or the API.

WordPress plugin

The plugin sends submissions straight to Ascue, without going through email. It covers Contact Form 7, WPForms (Lite and Pro), Gravity Forms and Elementor Pro forms.

  1. In WordPress, go to Plugins, then Add New, search for Ascue, and install and activate it. (Or upload the ascue folder to /wp-content/plugins/.)
  2. Go to Settings, then Ascue, paste your site key and press Save Changes.
  3. Press Test connection. You should see "Connected".
  4. Send a test message through one of your forms.

Until a site key is saved, the plugin does nothing and your forms work as before. Emails your form sends to the visitor, such as confirmations, aren't changed, and submissions with uploaded files keep your form's normal email so attachments always reach you.

When someone submits a form, the plugin sends it to Ascue. If Ascue accepts it, Ascue delivers the email according to its label and the plugin skips your form's own notification. If Ascue doesn't answer within 4 seconds, or answers with an error, the plugin sends your form's normal email instead. A slow day at Ascue never costs you a lead.

Form webhooks

For form tools that can send a webhook but can't set headers: Webflow's form submission webhook, Wix Automations, Zapier, Make and similar. Point the webhook at:

Webhook URL
https://api.ascue.dev/v1/forms/webhook?key=YOUR_SITE_KEY
  • The body can be Webflow's own payload, or any flat JSON object of form fields. Ascue finds the message, email, name and subject by their field names and keeps the rest as form fields.
  • Ascue answers 202 with the message's id, then sorts and delivers it like any other message. A wrong key gets 401 invalid_site_key; a body with no fields gets 400.
  • The URL contains your site key, so treat it like a password. If it leaks, rotate the key in the site's settings and update the webhook.

REST API

For developers and agencies: send messages from anything, then read the verdict by polling or a signed webhook. The base URL is https://api.ascue.dev. Requests and responses are JSON in camelCase, and times are ISO 8601 in UTC.

Authenticate with your site key in the X-Ascue-Site-Key header. Keep it on your server; anyone with it can send messages to your site.

Send a message

POST /v1/messages

curl
curl -X POST https://api.ascue.dev/v1/messages \
  -H "X-Ascue-Site-Key: $ASCUE_SITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi, do you have room for a group of 6 on Saturday morning?",
    "subject": "Booking question",
    "senderName": "Jenna Morales",
    "senderEmail": "jenna@example.com",
    "fields": { "phone": "555-0142" },
    "source": "api",
    "deliver": true
  }'
  • body is required, up to 20,000 characters.
  • subject, senderName, senderEmail and fields (other form fields, as strings) are optional.
  • source is api unless you're writing a plugin.
  • deliver defaults to true: Ascue emails the site's inbox address according to the verdict. Set it to false to only classify, and read the verdict yourself.
  • senderIp, senderForwardedFor and senderUserAgent are optional and let Ascue block bots attacking your form. Send the address that connected to your server, its X-Forwarded-For header if it sits behind a proxy or CDN, and the visitor's user agent. Ascue reads X-Forwarded-For only past private and Cloudflare addresses, and keeps only a keyed hash of the IP address.

Messages Ascue settles without asking its model don't use a check: ones its quick checks catch, ones it blocks during a spam attack, and an identical copy of a message the site got in the last 24 hours, which reuses the first one's answer.

Ascue answers within a few hundred milliseconds, before it has decided:

Response
HTTP/1.1 202 Accepted

{ "id": "7d0c5e8a-2f4b-4c1e-9a77-3b2f1e6d9c10", "status": "queued" }

A wrong or rotated key gets 401 invalid_site_key. If you're deciding whether to send your own notification, treat anything other than a 202, or no answer within a few seconds, as "Ascue didn't take it" and deliver the message yourself.

Get the verdict

GET /v1/messages/{id}/verdict, with the same X-Ascue-Site-Key header. While Ascue is deciding, status is queued and verdict is null. Ask again every second or so.

Node.js
const API = 'https://api.ascue.dev';
const headers = { 'X-Ascue-Site-Key': process.env.ASCUE_SITE_KEY };

// Ask every second until Ascue has decided, for up to 30 seconds.
async function waitForVerdict(id, timeoutMs = 30_000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const res = await fetch(`${API}/v1/messages/${id}/verdict`, { headers });
    if (!res.ok) throw new Error(`Ascue answered ${res.status}`);
    const { status, verdict } = await res.json();
    if (status !== 'queued') return { status, verdict };
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
  return null; // Still queued: treat it as unsorted and deliver it yourself.
}

Message statuses:

  • queued: Ascue is still deciding. The verdict is null.
  • delivered: Decided and sent to the inbox (or, with deliver: false, decided).
  • held: Decided as spam or a pitch and held back. Kept for 30 days.
  • delivered_unfiltered: Delivered without sorting, because Ascue couldn't decide in time.
  • over_quota: Arrived after the month's checks ran out, so it wasn't sorted.

Result webhooks

Rather than polling, add a result webhook URL in the site's settings. After deciding, Ascue POSTs the result there, signed with your site's webhook secret (also in the site's settings):

Request
POST /your/webhook/path
Content-Type: application/json
X-Ascue-Signature: sha256=5f1c0b...e9a2

{ "id": "7d0c5e8a-...", "siteId": "c2a9...", "status": "delivered", "verdict": { "label": "lead", ... } }

The X-Ascue-Signature header is sha256= followed by the hex HMAC-SHA256 of the raw request body, keyed with your webhook secret. Check it before trusting the body, and compare in constant time.

Node.js (Express)
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const secret = process.env.ASCUE_WEBHOOK_SECRET;

// Verify against the raw body, before any JSON parsing changes it.
app.post('/ascue/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const expected =
    'sha256=' + crypto.createHmac('sha256', secret).update(req.body).digest('hex');
  const received = req.get('X-Ascue-Signature') ?? '';

  const valid =
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
  if (!valid) return res.status(401).end();

  const { id, status, verdict } = JSON.parse(req.body.toString('utf8'));
  // verdict is null if the message was delivered unsorted.
  console.log(id, status, verdict?.label);
  res.status(204).end();
});

app.listen(3000);
PHP
<?php
$secret = getenv('ASCUE_WEBHOOK_SECRET');
$body = file_get_contents('php://input'); // The raw body, exactly as sent.

$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
$received = $_SERVER['HTTP_X_ASCUE_SIGNATURE'] ?? '';

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
$label = $event['verdict']['label'] ?? null; // null if delivered unsorted
// ... store or act on $event['id'], $event['status'], $label

http_response_code(204);

The verdict

Verdict
{
  "id": "7d0c5e8a-2f4b-4c1e-9a77-3b2f1e6d9c10",
  "status": "held",
  "verdict": {
    "label": "pitch",
    "confidence": 0.93,
    "isUrgent": false,
    "reason": "Pitch confidence 0.93",
    "scores": {
      "person": 0.98, "bot": 0.0, "unclear": 0.02,
      "buy": 0.04, "sell": 0.93, "support": 0.0, "job": 0.0, "other": 0.03,
      "spam": 0.05, "urgent": 0.01
    }
  }
}
  • label is lead, pitch, spam or ask_me.
  • confidence is how sure Ascue was, from 0 to 1.
  • isUrgent is true when the message reads as urgent.
  • scores holds the confidence for each answer to Ascue's four questions: who wrote it (person, bot, unclear), what they want (buy, sell, support, job, other), spam and urgent. It's null when a quick pre-check decided, for example a message with seven links.

Errors

Errors come back with an HTTP status and a body like { "error": "invalid_site_key", "message": "…" }: a code for your program and a sentence for a person.

Labels and settings

Lead

A real person writing to you: someone who wants to buy, book or hire, or a customer who needs help.

Delivered to your inbox right away.

Ask me

It might be a pitch or spam, but Ascue isn't sure.

Delivered to your inbox marked "Ascue wasn't sure". One click confirms or corrects it.

Pitch

Someone selling to you: SEO, dev outsourcing, guest posts, lead lists.

Held in the Pitch folder and listed in your weekly digest.

Spam

Bots, gibberish, scams, link drops.

Blocked, and kept for 30 days in case it's needed.

Each site has three plain-language settings. You can change them any time.

Strictness

  • Relaxed: Holds back only near-certain spam and pitches. More reaches your inbox.
  • Balanced: Holds back confident spam and pitches. Anything unsure reaches your inbox marked Ask me. Recommended.
  • Strict: Holds back more, and more messages land in Ask me for a quick yes or no.

Pitches

  • Hold for the weekly digest: Pitches skip your inbox and show up once a week in a list you can skim.
  • Deliver, tagged "Pitch": Pitches still reach your inbox, clearly marked so you can ignore them.

Ask me

  • Deliver, marked unsure: Reaches your inbox marked "Ascue wasn't sure", with one-click buttons to confirm or correct.
  • Hold until I review it: Waits in your dashboard until you say what it is.

Your corrections adjust a site's thresholds, but only within the strictness you picked, and Ascue tells you when they do. On the Agency plan you can also set custom thresholds per site.

Stuck? Email support@ascue.dev and a person will help, usually within two days.