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

# Custom webhook

> Receive JSON events from any app, identify the customer, and start Sagepilot journeys.

Custom webhooks let you POST JSON from a system Sagepilot does not integrate with natively. Sagepilot uses the payload to find or create a customer, stores the event, and can start or signal journeys.

This is an inbound webhook. Sagepilot does not send these events to you. For Sagepilot-to-your-app delivery, see [Event webhooks](/settings/event-webhooks). For a support messaging channel you own, see [Agent API](/api-reference/agent-api/overview).

## Before you start

* A Sagepilot workspace and permission to manage integrations.
* A JSON object your system can POST over HTTPS. The payload must include an email, a phone number, or both, unless you enable push registration.
* The sample payload you configure must match the live payload shape. Nested objects become journey variables; arrays stay as single values.

<Tip>
  Image placeholder: add a screenshot of **Settings → Integrations** with the **Custom Webhook** card, and the **Configure** tab after the integration is connected.
</Tip>

## How it works

1. You configure **Custom Webhook** once for the workspace, then add a named trigger.
2. You paste a sample JSON payload and map which fields are the customer's name, email, and phone.
3. Sagepilot gives you a unique POST URL for that trigger.
4. Each accepted POST is queued. Sagepilot then resolves the customer, records the event, and starts live journeys whose trigger is that event name.

A `200` response means Sagepilot accepted the request into the queue. It does not mean a customer was found or a journey started.

## Configure the integration

<Steps>
  <Step title="Open Integrations">
    In Sagepilot, go to **Settings → Integrations**. Custom Webhook is listed under **Webhooks & Ops**.
  </Step>

  <Step title="Connect Custom Webhook">
    Open **Custom Webhook** and click **Configure Custom Webhook**. You only do this once per workspace.
  </Step>

  <Step title="Open the trigger list">
    After setup, the page has **Analytics** and **Configure** tabs. Use **Configure** to add and manage triggers.
  </Step>
</Steps>

## Add a trigger

Click **Add trigger** and complete the three steps. Trigger names must be unique in the workspace. Sagepilot stores them as lowercase with spaces converted to underscores (`Lead Created` becomes `lead_created`).

<Steps>
  <Step title="Payload">
    Enter the trigger name and paste one JSON object exactly as your system will send it. The body must be an object, not an array.
  </Step>

  <Step title="Identity">
    Map payload fields to **Name** (optional), **Email**, and **Phone**. Map **Email**, **Phone**, or both, unless you enable [push registration](#register-android-push-tokens).
  </Step>

  <Step title="Variables">
    Review the fields Sagepilot will create as journey variables. Mapped contact fields become customer fields instead of extra variables. Adjust the format when Sagepilot infers it incorrectly, then click **Create trigger**.
  </Step>
</Steps>

After create, copy the **Webhook URL** and the sample `curl`. You can also copy both later from the trigger page.

<Tip>
  Image placeholder: add screenshots of the Add webhook trigger dialog (Payload, Identity, Variables) and the **Webhook ready** confirmation with the URL and sample curl.
</Tip>

## Send events

Copy the URL from the trigger page. The path is:

```text theme={null}
https://api.sagepilot.ai/webhooks/custom/{trigger_name}/{workspace_id}/{integration_id}
```

Treat the URL as a secret. Sagepilot authenticates the request with the workspace ID and integration ID in the path. There is no signature header or API key.

Always POST `application/json`. Do not send form-encoded bodies.

```bash theme={null}
curl -X POST \
  'https://api.sagepilot.ai/webhooks/custom/payment_failed/WORKSPACE_ID/INTEGRATION_ID' \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "email": "priya@example.com",
    "phone": "+919876543210",
    "name": "Priya Sharma",
    "order": {
      "id": "ORD-4821",
      "status": "payment_failed",
      "amount": 2499
    }
  }'
```

Use the sample curl from the dashboard so the URL and payload match the trigger you created.

### Request rules

| Rule               | Behavior                                                                                         |
| ------------------ | ------------------------------------------------------------------------------------------------ |
| Method             | `POST` only.                                                                                     |
| Body               | A JSON object. Arrays at the root are rejected.                                                  |
| Nested objects     | Flattened with dots. `order.id` in a `payment_failed` trigger becomes `payment_failed.order.id`. |
| Arrays and scalars | Kept as leaf values. Sagepilot does not expand array items into separate fields.                 |
| Idempotency        | None. Each POST gets a new event ID. Retrying the same payload can start another journey.        |

### HTTP responses

| Status | When                                                                                 |
| ------ | ------------------------------------------------------------------------------------ |
| `200`  | Sagepilot queued the webhook. Body: `{"message": "Webhook received and processed"}`. |
| `400`  | The body was not valid JSON or could not be decoded.                                 |
| `500`  | Sagepilot could not queue the event. Retry later.                                    |

## Identity mapping

Sagepilot finds or creates the customer from the mapped email and phone. Phone values are normalized before lookup. Name is optional and updates the customer profile when present.

If the live payload has neither a mapped email nor a mapped phone, Sagepilot does not record a customer event and does not start a journey. The HTTP response can still be `200` because acceptance happens before customer resolution.

## Journey variables

Unmapped payload fields become journey variables with source `custom`. In templates, decisions, and message actions they appear as:

```text theme={null}
{{ custom.payment_failed.order.id }}
```

Mapped identity fields use the Sagepilot customer variables instead:

```text theme={null}
{{ sagepilot.customer.email }}
{{ sagepilot.customer.phone }}
{{ sagepilot.customer.name }}
```

| Format         | Typical value                    | Decision operators                    |
| -------------- | -------------------------------- | ------------------------------------- |
| Text           | Strings and other non-URL values | `eq`, `neq`                           |
| Number         | JSON numbers                     | `eq`, `neq`, `gt`, `gte`, `lt`, `lte` |
| URL            | `http://` or `https://` links    | `eq`, `neq`                           |
| Image (URL)    | Image file URLs                  | Use in templates as media             |
| Document (URL) | Document file URLs               | Use in templates as media             |
| Video (URL)    | Video file URLs                  | Use in templates as media             |

Custom webhook variables are available in journey decisions and templates. They are not available as native segment attributes.

## Use in journeys

1. Publish at least one trigger.
2. Open **Engage → Journeys** and create a journey.
3. In the trigger node, choose the event under **Custom**. The event name matches the trigger name (`payment_failed`).
4. Use the payload variables in conditions, templates, and actions.

A later POST of the same trigger can start another live journey for that customer. It can also satisfy a **wait until event** step, or stop a running journey if you listed the event as a termination event.

<Tip>
  Image placeholder: add a screenshot of the journey trigger picker with the **Custom** source and a custom webhook event selected.
</Tip>

## Register Android push tokens

Optional. On the Identity step, enable **Push registration** to store Android FCM tokens from the payload.

* Select a [Push Notifications](/integrations/channels/push-notifications) channel that has Android / FCM configured.
* Map the FCM token field. This is required when push registration is on.
* Optionally map an external user ID field.

When push registration is enabled, email and phone are optional. Sagepilot still records the token. It attaches the token to the customer when identity is present, and can attach it later when a later event resolves the same token.

Push registration from custom webhooks is Android FCM only.

## Monitor events

On the Custom Webhook home page:

* **Analytics** shows volume over 24 hours, 7 days, or 30 days, counts by event type, and payload history.
* **Configure** lists triggers. Open a trigger for its webhook URL, identity mapping, sample payload, event trend, and recent events.

Recent events and analytics only include payloads that resolved a customer. Token-only posts that never include email or phone do not appear there.

## Update a trigger

Open the trigger, edit the sample payload, identity mapping, variable formats, or push registration, then click **Save changes**.

* New fields become new journey variables.
* Fields you remove are deleted as journey variables. Journeys that still reference those variables will not resolve them.
* You cannot rename a trigger. Create a new trigger if you need a new URL path.

## Troubleshooting

| Issue                                    | What to check                                                                                           |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `400` from the webhook                   | Send `Content-Type: application/json` and a JSON object, not an array or form body.                     |
| `200` but no journey and no recent event | The payload did not include the mapped email or phone, or those fields were empty.                      |
| Trigger not in the journey picker        | Confirm the trigger was created, then refresh the journey builder.                                      |
| Variable missing in a template           | The live payload path must match the sample. Nested keys are dotted; arrays are not exploded.           |
| Duplicate journeys                       | Each POST is a new event. Deduplicate in your sender, or turn off concurrent traversals on the journey. |
| Push token not stored                    | Enable push registration, map the token field, and select an Android FCM push channel.                  |

## Related

<CardGroup cols={2}>
  <Card title="Event webhooks" href="/settings/event-webhooks">
    Send Sagepilot events outbound to your HTTPS endpoint.
  </Card>

  <Card title="Agent API" href="/api-reference/agent-api/overview">
    Send customer messages from a channel you own and receive replies.
  </Card>

  <Card title="Push notifications" href="/integrations/channels/push-notifications">
    Create the Android FCM channel used by webhook token registration.
  </Card>

  <Card title="Journeys" href="/engage/journeys">
    Start automations from custom webhook triggers.
  </Card>
</CardGroup>
