> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cedarai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflow Effect Webhooks

> Real-time event notifications delivered as signed HTTP POST requests

## Overview

Cedar's workflow automation can send real-time event notifications to your systems via **webhooks**. When a configured event occurs (such as a car being placed at a station), Cedar POSTs a signed JSON payload to your endpoint.

<Info>
  Webhooks let you integrate Cedar events into your own systems -- ERP platforms, notification services, or any HTTP
  endpoint that can receive JSON.
</Info>

***

## How It Works

```mermaid theme={null}
flowchart LR
    Event[Workflow Event] --> Effect[Webhook Effect]
    Effect --> Sign[Sign Payload]
    Sign --> POST[HTTP POST]
    POST --> Endpoint[Your Endpoint]
    Endpoint --> Verify[Verify Signature]
```

<Steps>
  <Step title="Event Triggers">A workflow event fires in ARMS (e.g. car placed at a station).</Step>

  <Step title="Payload Built">
    Cedar constructs a JSON payload with event details, equipment information, and timestamps.
  </Step>

  <Step title="Signed & Sent">The payload is signed with Ed25519 and POSTed to your configured webhook URL over HTTP/2 (with automatic fallback to HTTP/1.1).</Step>

  <Step title="You Verify">
    Your endpoint verifies the signature using Cedar's public key, then processes the event.
  </Step>
</Steps>

***

## Configuration

Each webhook effect requires one input when you set up the workflow in ARMS:

| Input           | Description                                            |
| --------------- | ------------------------------------------------------ |
| **Webhook URL** | The HTTPS endpoint where Cedar sends the POST request. |

<Warning>
  Always use HTTPS endpoints in production.
</Warning>

***

## HTTP Headers

Every webhook request includes these headers:

| Header                | Description                                    | Example                  |
| --------------------- | ---------------------------------------------- | ------------------------ |
| `Content-Type`        | Always `application/json`                      | `application/json`       |
| `X-Webhook-Timestamp` | Unix epoch (seconds) when the request was sent | `1709651400`             |
| `X-Webhook-Signature` | Base64-encoded Ed25519 signature               | `a3F0eGp...`             |
| `X-Webhook-KeyId`     | Identifier for the signing key used            | `cedar-webhooks-2026`    |
| `X-Webhook-Id`        | UUID v4 for deduplication                      | `550e8400-e29b-41d4-...` |

***

## Signature Verification

Cedar signs every webhook payload with **Ed25519** so you can verify that requests genuinely came from Cedar and haven't been tampered with.

### Public Key

**Use this public key to verify webhook signatures:**

```
MCowBQYDK2VwAyEAuePoYCHOJvZJzlnsxfEv3mtssVKxkDAZsDHUE9Z3TW8=
```

<Tip>Store this public key in your application configuration. It does not change between environments.</Tip>

### How Signing Works

The signature covers a combination of the timestamp, key ID, and the request body:

```
signing_input = TIMESTAMP + "." + KEY_ID + "." + CANONICAL_JSON_BODY
```

Where:

* **TIMESTAMP** is the value of the `X-Webhook-Timestamp` header (string)
* **KEY\_ID** is the value of the `X-Webhook-KeyId` header (string)
* **CANONICAL\_JSON\_BODY** is the raw request body bytes (JSON with sorted keys and no extra whitespace)

The signature is the Ed25519 signature of `signing_input`, base64-encoded in the `X-Webhook-Signature` header.

***

## Security Best Practices

<AccordionGroup>
  <Accordion title="Verify every request" icon="shield-check" defaultOpen>
    Always verify the `X-Webhook-Signature` before processing the payload. This confirms the request came from Cedar and hasn't been modified in transit.
  </Accordion>

  {' '}

  <Accordion title="Check timestamps for replay protection" icon="clock">
    Compare `X-Webhook-Timestamp` against the current time. Reject requests older than 5 minutes to prevent replay
    attacks.
  </Accordion>

  {' '}

  <Accordion title="Use X-Webhook-Id for deduplication" icon="copy">
    Network retries can cause duplicate deliveries. Store processed `X-Webhook-Id` values and skip duplicates.
  </Accordion>

  <Accordion title="Respond quickly" icon="bolt">
    Return a `2xx` response within 30 seconds. If processing takes longer, acknowledge the request immediately and process asynchronously.
  </Accordion>
</AccordionGroup>

***

## Shared Payload Fields

Every webhook payload includes the following top-level fields in addition to the event-specific ones documented on each webhook's page.

### actor

Identifies the user or system actor that caused the event to fire. Useful for auditing, attribution, and downstream filtering.

```json theme={null}
"actor": {
  "resource_type": "User",
  "resource_id": 12345,
  "display_name": "ops@customer.com"
}
```

| Field           | Type              | Description                                                                                                                            |
| --------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `resource_type` | string            | The kind of actor. See the table below for known values.                                                                               |
| `resource_id`   | integer or string | The actor's internal identifier. Type depends on `resource_type` -- typically integer for users and carriers, string for X12 messages. |
| `display_name`  | string            | Human-readable label for the actor. Best-effort; falls back to a stable synthetic label when no name is available.                     |

Known `resource_type` values:

| `resource_type`      | When emitted                                                               | `display_name` source                        |
| -------------------- | -------------------------------------------------------------------------- | -------------------------------------------- |
| `User`               | A user triggered the event directly (e.g. placing a car in the UI).        | `display_name`, falling back to `email`.     |
| `WorkflowTrigger`    | Another workflow effect fired this event transitively.                     | Workflow trigger name.                       |
| `WorkflowInvocation` | A specific workflow invocation produced the event (cascading invocations). | Workflow invocation name.                    |
| `Carrier`            | An automated carrier-level process (not a human) initiated the event.      | Carrier name, falling back to carrier code.  |
| `X12Message`         | An inbound EDI (X12) message drove the state change that fired the event.  | `X12Message<{resource_id}>` synthetic label. |

<Note>
  `actor` is `null` for cron-scheduled workflows or other system-initiated events that have no identifiable actor. The `actor` key is always present in the payload, so your integration can rely on it being set (possibly to `null`).
</Note>

***

## Retry Behavior

Cedar automatically retries webhook deliveries when transient failures occur. Retries happen in two phases: inline retries during the initial delivery, and deferred retries scheduled over the next 24 hours.

### Inline retries

| Condition                        | Behavior                                           |
| -------------------------------- | -------------------------------------------------- |
| **Server errors (5xx)**          | Retried up to 5 total attempts                     |
| **Timeouts & network errors**    | Retried up to 5 total attempts                     |
| **HTTP 408 and 429**             | Retried up to 5 total attempts                     |
| **Other client errors (4xx)**    | Not retried inline -- fix the issue and redeploy   |
| **TLS / SSL certificate errors** | Not retried -- the endpoint certificate is invalid |

Inline retries use exponential backoff (1--10 seconds between attempts). The first attempt uses a 30-second timeout; retries use a shorter 5-second timeout. If HTTP/2 negotiation fails with a protocol error, Cedar automatically downgrades to HTTP/1.1 on the next attempt.

### Deferred retries

If all inline attempts fail, Cedar schedules deferred retries on an hourly cadence:

* Retried once per hour for up to **24 hours** or **8 attempts**, whichever comes first.
* Each deferred attempt is re-signed with a **fresh `X-Webhook-Timestamp`** (because receivers reject stale signatures) but **keeps the same `X-Webhook-Id`** so your endpoint can deduplicate.
* After the retry window closes, the delivery is marked **abandoned** and no further attempts are made.

<Info>
  The same `X-Webhook-Id` is sent on every inline and deferred retry attempt. Store processed IDs on your side to deduplicate safely.
</Info>

***

## Available Webhook Effects

<CardGroup cols={2}>
  <Card title="Car Actually Placed" icon="train" href="/user-docs/arms/webhooks/car-actually-placed">
    Fires when a car is placed at a station or track. Includes equipment details, station, and loaded/empty status.
  </Card>

  <Card title="Train Arrival" icon="train" href="/user-docs/arms/webhooks/train-arrival">
    Fires when a train arrives at a station. Includes equipment list, station, loaded/empty status, and train number.
  </Card>

  <Card title="Train Set" icon="train-tram" href="/user-docs/arms/webhooks/train-set">
    Fires when a train consist is defined. Includes wagon list, departure/arrival stations with names, and train ID.
  </Card>

  <Card title="Train Departure" icon="train" href="/user-docs/arms/webhooks/train-departure">
    Fires when a train departs from a station or customer location. Includes equipment list, station, customer/serving location, loaded/empty status, and train number.
  </Card>
</CardGroup>

***

## Questions?

<Tip>
  For questions about webhook configuration or integration, contact your Cedar account team or email
  **[support@cedarai.com](mailto:support@cedarai.com)**.
</Tip>
