Webhook data push
Webhook push is the inbound counterpart to remote data sources. Instead of MMTable polling an upstream URL, an external system pushes updates to MMTable. Useful for event-driven flows: "when a new lead is captured, append to the leads table".
Enable webhooks on a table
- Open the table in the editor.
- Open the Webhook Push section.
- Click Generate Secret. A 32-character random secret appears.
- Save the table.
The endpoint for this table is now:
POST /wp-json/mm-table/v1/tables/<id>/webhook
Where <id> is the table's post ID (shown at the top of the edit screen).
Sign the request
Every webhook POST must include an HMAC-SHA256 signature in the X-MMT-Signature header, formatted as sha256=<hex>. The signature is computed over the raw request body using the table's secret as the HMAC key.
In bash:
SECRET=abc123...
BODY='{"rows":[["Apple","2.99"],["Pear","3.49"]]}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -X POST https://example.com/wp-json/mm-table/v1/tables/42/webhook
-H "Content-Type: application/json"
-H "X-MMT-Signature: sha256=$SIG"
-d "$BODY"
Body formats
Two shapes are accepted:
Replace rows only — keeps headers, formatting, settings intact:
{
"rows": [
["Apple", "2.99"],
["Pear", "3.49"],
["Cherry", "4.99"]
]
}
Full settings replacement — overwrites the entire saved table:
{
"data": { ...full settings object... }
}
Either shape can also include a title field to rename the table at the same time.
Body size limit
Payloads larger than 500 KB are rejected with HTTP 413. For larger imports, split into multiple webhook calls or use the remote CSV data source.
Rotating or disabling the secret
On the table's edit screen, the Webhook Push section offers:
- Rotate secret — generates a new secret. Old requests stop verifying immediately.
- Disable webhook — clears the secret. The endpoint returns 403 until a new secret is generated.
Rotate after a suspected leak. Disable when the integration goes away.
Security model
- The endpoint is public (no WP login required) — that's the point of webhooks. Authentication is HMAC-only.
- Signature comparison is timing-safe (
hash_equals), so an attacker can't probe the secret one byte at a time. - If the secret is empty or unset, all requests fail closed — there's no "no signature = allow" mode.
- Cell content in the payload runs through
wp_kses_postafter verification, so even a compromised webhook key can't inject<script>tags. - No replay protection. Same valid (body, signature) pair can be POSTed indefinitely. For mutating webhooks this is usually fine; for "credit account" style flows, add an idempotency key to the body and check it before applying.
Integration recipes
Zapier: use the "Webhook by Zapier" action. POST step → Custom Request. Body = JSON. Add a Code step before the POST to compute the HMAC signature with your Zapier secret as input.
Make (Integromat): HTTP module → Make a request. Set the URL, method POST, body type JSON, headers including X-MMT-Signature. Compute the HMAC in a preceding "Tools → Set variable" step using hmac_sha256().
n8n: HTTP Request node with method POST. Use the Function node before it to compute the signature with crypto.createHmac('sha256', secret).update(body).digest('hex').