API

Webhook ingestion

POST external events to a per-trigger webhook URL to start GTM Engine workflows.

Webhook triggers give you a unique inbound endpoint that any external system can POST JSON to. Each delivery is stored and can start one or more workflows. Because this is a code integration you drive from your own systems (marketing site, product backend, form provider, etc.), the contract is documented here alongside the REST API.

This endpoint is a receiver, not part of the CRM REST API. It is not authenticated with a Bearer API key and is intentionally not listed in the OpenAPI spec. It authenticates with the trigger's configured secret mode instead (see Authentication).

Endpoint

POST https://api.gtmengine.ai/webhooks/trigger/{triggerId}
Content-Type: application/json
  • {triggerId} — the UUID of your webhook trigger. Create a webhook trigger in the app (Automation → Triggers) and copy its endpoint URL; the triggerId is the last path segment.
  • The endpoint is mounted at the host root (not under /v1).
  • Only POST is accepted.

Authentication

Every webhook trigger requires a secret — there is no unauthenticated mode. Send it in the X-Webhook-Secret header:

X-Webhook-Secret: your-secret-or-webhook-key
ModeHeader requiredHow to obtain
Shared webhook keyX-Webhook-SecretCreate a key on the API Keys page (/automation/api) with the Webhook permission, then select that key on the trigger. Only the selected key is accepted; the same key can be selected for many triggers.
Unique secret for this triggerX-Webhook-SecretA dedicated secret generated for this trigger, shown once at creation. It's stored only as a hash — rotate the trigger to generate a new one if lost.

Requests that omit or present an invalid secret are rejected with 401.

Request body

The body must be a JSON object (not an array or primitive). An empty body is treated as {}.

{
  "event": "newsletter_signup",
  "user": {
    "email": "jordan@example.com",
    "name": "Jordan Lee"
  },
  "form": {
    "company": "Acme Inc."
  }
}
  • Maximum body size: 512 KB. Larger requests are rejected with 413.
  • Structural limits: payloads with more than 1,000 total keys or nested deeper than 20 levels are rejected with 400.
  • The full body is stored and flattened into dot-notation fields (e.g. payload.user.email, payload.form.company) that you map to workflow inputs. See Using the payload as variables.

Responses

StatusBodyMeaning
202 Accepted{ "received": true }Delivery accepted, stored, and enqueued.
400 Bad Request{ "error": "Request body must be valid JSON." }Body was invalid, exceeded structural limits, or did not satisfy the required inputs for an assigned workflow.
401 Unauthorized{ "error": "Missing or invalid webhook secret." }A secret was required but missing/invalid.
404 Not Found{ "error": "Webhook trigger not found." }No active webhook trigger with that ID.
413 Payload Too Large{ "error": "Payload too large." }Body exceeded 512 KB.
429 Too Many Requests{ "error": "Rate limit exceeded. Please try again later." }Too many requests for this trigger; back off and retry.

A 202 means the delivery was accepted and stored — the downstream workflow runs asynchronously. Don't retry on 202. Retrying on 5xx is safe.

Examples

cURL

curl -X POST "https://api.gtmengine.ai/webhooks/trigger/8d88fcdc-cdc6-4ca9-9c1c-acf4d183dd12" \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Secret: your-secret-or-webhook-key" \
  -d '{"event":"newsletter_signup","user":{"email":"jordan@example.com"}}'

JavaScript (fetch)

await fetch(
  "https://api.gtmengine.ai/webhooks/trigger/8d88fcdc-cdc6-4ca9-9c1c-acf4d183dd12",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Webhook-Secret": process.env.GTM_WEBHOOK_SECRET,
    },
    body: JSON.stringify({
      event: "newsletter_signup",
      user: { email: "jordan@example.com" },
    }),
  },
);

Next steps

  • Triggers — how webhook triggers fit into the trigger model and how payload fields become variables.
  • Variables — mapping webhook payload fields into workflow inputs.

On this page