Skip to content

Incoming webhook

How another system (CRM, form, ERP, n8n, Zapier) starts a community workflow by calling a Memberfy address. Creating, rotating and revoking the address, the token in the header, the HMAC signature with curl and Node examples, how the person is found, the trigger variables, the responses and the deliveries.

The incoming webhook is the reverse of webhooks: instead of Memberfy notifying another system, another system (a CRM, a form, an ERP, an automation in n8n or Zapier) calls a Memberfy address and starts a workflow for a person in the community. For example: the CRM closes a sale and the person receives the customer welcome series.

Create the address

Role: owner or admin.

  1. In Administration › Workflows, open the workflow and drag the Webhook trigger block (Webhook received) onto the canvas. Each workflow has a single trigger.
  2. In Who the person is, say where the person is in the call's JSON: By e-mail or By memberId, and the JSON path (fields separated by dots, like contact.email, no list indexes).
  3. In Values to use in the steps, if you like, map up to 20 values from the JSON (Name and path).
  4. Save the workflow and, in the trigger panel, click Create address.

The window shows the Address, the Token and the Signing secret (HMAC) only once: "Save them now: the token and the secret are shown only this once." After that, the panel shows only the end of the token and of the secret.

  • Rotate creates a new token and secret, which take effect at once; the system calling the current address stops working until it gets the new ones.
  • Revoke turns the address off at once: calls start being refused.

Call the address

Make a POST with a JSON body (Content-Type: application/json, up to 64 KB) to https://api.memberfy.net/api/workflow-hooks, with:

HeaderWhat it is
X-Memberfy-Hook-TokenThe trigger's token. Always send it in the header: in the address (/api/workflow-hooks/{token}) it also works, but it ends up in logs
X-Memberfy-SignatureThe signature, t=<unix>,v1=<hex> (see below)
X-Memberfy-Delivery-IdA unique identifier for the call (up to 64 letters, digits, - and _). Repeated within 10 minutes, the call is refused

The signature (HMAC)

With Require a signature (HMAC) on (the default), only whoever holds the secret can start the workflow. The signature is mandatory when the trigger's path sends content (e-mail, message, comment, reaction or Slack): without it, the workflow does not turn on.

To sign:

  1. t is the current time in seconds (Unix). It is valid for 5 minutes either way.
  2. Compute the HMAC-SHA256, in hexadecimal, with the secret, over t, a dot and the raw body, exactly the bytes that go in the call: "<t>.<body>".
  3. Send X-Memberfy-Signature: t=<t>,v1=<hmac>.

curl:

SECRET='the-trigger-secret'
TOKEN='the-trigger-token'
BODY='{"customer":{"email":"[email protected]"},"plan":{"name":"Anual"}}'
T=$(date +%s)
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')

curl -X POST https://api.memberfy.net/api/workflow-hooks \
  -H 'Content-Type: application/json' \
  -H "X-Memberfy-Hook-Token: $TOKEN" \
  -H "X-Memberfy-Signature: t=$T,v1=$SIG" \
  -H "X-Memberfy-Delivery-Id: crm-$(date +%s%N)" \
  --data "$BODY"

Node:

const crypto = require('crypto');

async function startWorkflow(payload) {
  const body = JSON.stringify(payload); // sign exactly what goes in the call
  const t = Math.floor(Date.now() / 1000);
  const v1 = crypto
    .createHmac('sha256', process.env.MEMBERFY_HOOK_SECRET)
    .update(`${t}.${body}`)
    .digest('hex');

  const res = await fetch('https://api.memberfy.net/api/workflow-hooks', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Memberfy-Hook-Token': process.env.MEMBERFY_HOOK_TOKEN,
      'X-Memberfy-Signature': `t=${t},v1=${v1}`,
      'X-Memberfy-Delivery-Id': crypto.randomUUID(),
    },
    body,
  });
  return res.status; // 202 when accepted
}

startWorkflow({ customer: { email: '[email protected]' }, plan: { name: 'Anual' } });

The person and the variables

  • Memberfy looks for the person after responding, by the e-mail or the memberId at the configured path. They must be a member of the community; otherwise, the delivery shows as Person not found.
  • Only the mapped values are kept, in the person's run; the rest of the body is discarded.
  • Each value becomes the variable {{gatilho_<nome>}}, for example {{gatilho_plano}}. It works in the text of messages, comments and Slack and in the body of e-mails, never in the subject or inside links.
  • How many times a person enters works as in any trigger, and each person enters at most 5 times a day through each workflow's webhook.
  • A call that carries the X-Memberfy-Run-Id header of a run of the same workflow (an outside system echoing back the call the workflow made) is ignored, to avoid a loop. A workflow's Slack posts carry that header.

Responses

StatusWhen
202Token and signature are valid. The response is the same whether or not the person is found; the result shows in the deliveries
401Signature missing, wrong or outside the 5 minutes
404Token unknown, revoked or from a deleted workflow
409X-Memberfy-Delivery-Id repeated within 10 minutes
413Body over 64 KB
429More than 60 calls a minute or 5,000 a day on the same address; wait for the Retry-After
503The repeat check is unavailable right now; try again

Deliveries

In the trigger panel, Deliveries lists each call received, with the result: Started, Queued, Person not found, Workflow off, Already entered, Person’s daily limit, Unsigned or Repeated call (loop).

Through the assistant (MCP)

The owner and the admin create the address (create_workflow_webhook: the token and the secret are shown once), rotate and revoke it (rotate_workflow_webhook and revoke_workflow_webhook, with confirmation); the team looks up the address (get_workflow_webhook, never the token) and the deliveries (list_workflow_webhook_deliveries). See Tools · Community.