Elizon Docs
Workflows

Webhook trigger

Let another system start a run by POSTing to a URL unique to your deployment.

The Webhook trigger (trigger_http, shown in Studio as the HTTP Request trigger) gives your published workflow a URL that another system can POST to. Its field list is the request body the workflow expects, checked before the run starts.

Configuration

FieldTypeDefaultDescription
methodGET | POST | PUT | PATCH | DELETEPOSTThe HTTP method the trigger documents.
fieldslist of { name, type, required }[]The request-body fields. type is string, number, boolean or object. Studio shows the matching OpenAPI request body beside them.
webhookSecretstring''The signing secret for HMAC-signed calls. Studio shows it next to the URL; treat it as a credential.

Declared fields are checked when the run starts: a request missing a required field, or sending the wrong type, fails the run before any node runs, naming each field. The check applies only when this trigger is the one that fired — a scheduled run of the same workflow is not rejected for a missing body.

Lets a third-party system that has no Elizon API key — a payment provider, a CRM, a form backend — start a run by POSTing to a URL unique to your deployment.

POST /api/v1/workflows/{workflowId}/trigger/{webhookToken}

The webhookToken is opaque and is never the workflow's own id. It is carried forward unchanged every time you republish — only a workflow that has never been published mints a new one — so a URL you have already handed to a third-party system keeps working across deploys (see Publishing). Studio shows you both the URL and its signing secret. Add a Webhook trigger node (trigger_http) to the graph so the payload has somewhere to land.

Authenticating a webhook call

Either mechanism is sufficient on its own:

HMAC signature. Send two headers:

HeaderValue
X-Elizon-TimestampUnix seconds, as sent
X-Elizon-SignatureHMAC-SHA256(secret, "<timestamp>.<raw body>"), hex

The timestamp is signed inside the payload, not merely sent alongside it, so a captured request cannot be replayed with a fresh timestamp header. Requests whose timestamp is more than 5 minutes from the server's clock are rejected. Sign the exact bytes of the header and the raw body — re-serializing either will produce a signature that does not match.

import hashlib, hmac, json, time, httpx

secret = "whsec_..."          # from Studio
body = json.dumps({"orderId": "A-1001"})
ts = str(int(time.time()))
sig = hmac.new(secret.encode(), f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()

httpx.post(
    f"https://api.elizon.ai/api/v1/workflows/{workflow_id}/trigger/{token}",
    headers={
        "Content-Type": "application/json",
        "X-Elizon-Timestamp": ts,
        "X-Elizon-Signature": sig,
    },
    content=body,
)

API key. Send X-API-Key: elz_... (or the equivalent Authorization: Bearer elz_...) and no signature is required. The key must belong to the workflow's own organization, and a project-scoped key to the workflow's project.

X-Elizon-Timestamp is required either way

Replay protection is applied to both authentication paths, so X-Elizon-Timestamp is mandatory even when you authenticate with an API key and send no signature at all. A request without it — or with one outside the window above — gets exactly the same uninformative 401 as a bad signature.

Webhook responses

StatusMeaning
202Accepted — body is { "executionId": "..." }. The run proceeds in the background.
400Authenticated, but the body is not valid JSON; or the body holds more than 20,000 values (array elements plus object keys across the whole body, each array or object counting 5 more) or nests deeper than 256 levels, which is refused before authentication.
401Neither the signature nor the key authenticated the request.
404No active deployment matches that token — including an unpublished or deleted workflow.
413Body exceeded the 1 MB limit.
429Rate limit, or the concurrency ceiling.

404 is deliberately uninformative

An unknown token, a token belonging to another organization, and a token whose workflow has been unpublished all return the identical 404 body. That is intentional — a webhook URL is a credential, and distinguishing those cases would let a caller probe for valid ones.

Because the body arrives under body, a webhook-driven graph reads its payload as {{ trigger.body.orderId }} — see Output below.

Output

The node's output is the run's input. A webhook call nests the request body under body:

ReferenceValue
{{ trigger.body.<field> }}A field of the request body, for example {{ trigger.body.orderId }}.
{{ run.trigger.type }}webhook for a webhook call; api when the same workflow is run through the public API.
{{ run.trigger.id }}This node's id.

A run started through POST /workflows/{id}/run binds your input object flat, so the same field is {{ trigger.orderId }} there. The declared fields are checked either way.

Limits and errors

  • One per workflow. The URL is issued once per workflow, so a second Webhook trigger is refused at save.
  • Bodies over 1 MB are refused with 413.
  • Bodies over 20,000 values (array elements plus object keys across the whole body, each array or object counting 5 more) or nested deeper than 256 levels are refused with 400, even under 1 MB.
  • See Webhook responses for every status code.

Example

workflows/new-order.yaml
name: New order
description: Handles an order posted by the shop.
graph:
  nodes:
    - id: order-received
      type: trigger_http
      position: { x: 0, y: 0 }
      data:
        method: POST
        fields:
          - { name: orderId, type: string, required: true }
          - { name: total, type: number, required: true }
    - id: check-order
      type: ai_agent
      position: { x: 280, y: 0 }
      data:
        mode: existing
        agent: agents.order-checker
        agentName: Order checker
        inputMappings: []
  edges:
    - { id: order-to-check, source: order-received, target: check-order }

The agent reads {{ trigger.body.orderId }} and {{ trigger.body.total }}.

In a Package

Declare a webhook trigger in the manifest, with the payload it documents:

elizon.yaml
triggers:
  - id: new-order
    type: webhook
    payload:
      fields:
        - { name: orderId, type: string, required: true }
        - { name: total, type: number, required: true }

The webhook URL and its token are generated when the installer activates the Package. On the node itself:

  • Travels: method, fields
  • Converted: none
  • Never carried: webhookSecret

See Package triggers.