Installer.com Docs

Webhooks

Send data from workflow steps to external systems on state changes

Overview

Webhooks in Installer.com are configured on individual workflow steps and fire when a step transitions to a specific state. Each webhook sends an HTTP request with a JSON payload that you define using Handlebars templates, allowing you to control exactly what data is sent to your external system.

How to configure a webhook

Open the workflow editor

Go to Workflows in the Routing App. Open the workflow template you want to add a webhook to, or create a new one.

Select a step and add a notification

Click on a workflow step, then navigate to the Notifications tab. Click Add and select Webhook.

Choose the triggering state

Select which step state should trigger the webhook (e.g. active, done, rejected). See Workflow states for the full list.

Configure timing

Choose when the webhook fires relative to the state change:

TimingDescription
On stateFires immediately when the step enters the selected state
After X daysFires X days after the step enters the selected state
Before X daysFires X days before a booking date (only for steps connected to a booking)

Set the URL, payload, and headers

  • URL: The endpoint to call. Supports Handlebars variables (e.g. https://api.example.com/orders/{{order.id}}).
  • Method: HTTP method (default: POST). Use GET to skip the request body.
  • Payload: A JSON body using Handlebars templates. See Handlebars syntax for details.
  • Headers: Key-value pairs sent with the request. Values support Handlebars templates.

Handlebars syntax

Webhook payloads, URLs, and headers are rendered with the Handlebars templating language. JSON strings containing Handlebars expressions ({{ }}) are replaced with actual values at execution time.

Because the standard Handlebars engine is used, all of its built-in helpers are available — including {{#each}} for iteration and {{#if}} for conditionals — in addition to the custom unix helper.

Basic variable interpolation

Use {{variableName}} or {{object.property}} to insert values:

{
  "orderId": "{{order.id}}",
  "orderName": "{{order.name}}",
  "customerEmail": "{{order.email}}"
}

Nested access

Access deeply nested properties with dot notation:

{
  "installerName": "{{actor.primary-installer.organizationName}}",
  "installerEmail": "{{actor.primary-installer.organizationResponsibleEmail}}"
}

Variable values must be wrapped in double quotes to produce valid JSON. Writing "id": {{order.id}} (without quotes around the value) will result in invalid JSON and a failed webhook.

The unix helper

Use the unix helper to convert ISO date strings to Unix timestamps (seconds since epoch):

{
  "bookingStart": "{{unix booking.startAt}}",
  "bookingEnd": "{{unix booking.endAt}}"
}

Iterating over offer lines

Use the built-in {{#each}} helper to loop over arrays such as offer.lines. When building a JSON array, use {{#unless @last}},{{/unless}} to add commas between items but not after the last one:

{
  "orderId": "{{order.id}}",
  "lines": [
    {{#each latestOffer.lines}}
    {
      "name": "{{this.name}}",
      "quantity": "{{this.quantity}}",
      "unitAmount": "{{this.payerUnitAmount}}",
      "productId": "{{this.productId}}",
      "externalId": "{{this.externalId}}"
    }
    {{#unless @last}},{{/unless}}
    {{/each}}
  ]
}

Inside an {{#each}} block, this refers to the current item.

Full payload example

{
  "orderId": "{{order.id}}",
  "displayId": "{{order.displayId}}",
  "orderName": "{{order.name}}",
  "customerName": "{{order.contactPersonName}}",
  "customerEmail": "{{order.email}}",
  "customerPhone": "{{order.phoneNumber}}",
  "address": "{{order.address}}",
  "city": "{{order.city}}",
  "postalCode": "{{order.postalCode}}",
  "countryCode": "{{order.countryCode}}",
  "state": "{{state}}",
  "timestamp": "{{timestamp}}",
  "bookingStart": "{{unix booking.startAt}}",
  "bookingEnd": "{{unix booking.endAt}}",
  "installerOrg": "{{actor.primary-installer.organizationName}}",
  "installerContact": "{{actor.primary-installer.organizationResponsibleName}}",
  "offer": [
    {{#each latestOffer.lines}}
    {
      "name": "{{this.name}}",
      "quantity": "{{this.quantity}}",
      "unitAmount": "{{this.payerUnitAmount}}",
      "productId": "{{this.productId}}",
      "externalId": "{{this.externalId}}"
    }
    {{#unless @last}},{{/unless}}
    {{/each}}
  ]
}

Workflow states

A webhook is triggered when its step enters the configured state. The following states are available:

StateDescription
activeThe step is now the current step and requires action
doneThe step has been completed successfully
skippedThe step was skipped
rejectedThe step was rejected (e.g. a dispatching was declined)
cancelledThe step was cancelled
expiredThe step expired without being completed in time
unconfirmedThe step is awaiting confirmation (e.g. dispatching sent to contractor)
waitingThe step is waiting for a future event (e.g. a scheduled booking)
overdueThe step was not completed within the expected time period

The state variable is automatically injected into the template context with the value of the triggering state.

Available variables

All variables below can be used in the URL, payload, and header templates. Variables are populated from the order and workflow data at the time the webhook fires.

Order

VariableTypeDescription
order.idstringOrder UUID
order.displayIdstringHuman-readable order ID (e.g. P1-42)
order.namestringOrder name
order.emailstringOrder contact email
order.phoneNumberstringOrder contact phone number
order.contactPersonNamestringFull name of the contact person
order.firstNamestringFirst name (derived from contactPersonName)
order.lastNamestringLast name (derived from contactPersonName)
order.addressstringStreet address
order.citystringCity
order.postalCodestringPostal code
order.countryCodestringCountry code (e.g. NO, NL)
order.tokenstringOrder token
order.createdAtstringCreation timestamp

Workflow

VariableTypeDescription
workflow.idstringWorkflow UUID
workflow.orderIdstringAssociated order UUID

Router (organization)

VariableTypeDescription
router.idstringRouter organization UUID
router.namestringOrganization name
router.slugstringOrganization slug
router.logoFileIdstringLogo file identifier
router.localestringOrganization locale
router.timezonestringOrganization timezone
router.senderIdstringSMS sender ID
router.defaultEmailSenderNamestringDefault email sender name
router.addressstringStreet address
router.citystringCity
router.postalCodestringPostal code
router.countryCodestringCountry code (e.g. NO, NL)
router.statestringState / region subdivision code (e.g. CA, NY)
router.legalNamestringLegal organization name
router.legalOrgIdstringLegal organization ID (e.g. company registration number)

Responsible routing operator

VariableTypeDescription
responsibleRoutingOperator.firstNamestringOperator first name
responsibleRoutingOperator.lastNamestringOperator last name
responsibleRoutingOperator.namestringFull name (first + last)
responsibleRoutingOperator.emailstringOperator email
responsibleRoutingOperator.phoneNumberstringOperator phone number
responsibleRoutingOperator.languagestringPreferred language

Booking

Only available if a BookingCreate step exists in the workflow and is connected to the current step or its dependencies.

VariableTypeDescription
booking.idstringBooking UUID
booking.startAtstringBooking start time (ISO 8601)
booking.endAtstringBooking end time (ISO 8601)
booking.typestringBooking type (e.g. installation, inspection)

Offer / Latest offer

Only available if an OfferCreate step has been completed (status = Done) before the current step.

offer is the offer connected to the current step's group. latestOffer is the most recently completed offer regardless of step group.

VariableTypeDescription
offer.idstringOffer UUID
offer.namestringOffer name
offer.statusstringOffer status
offer.payerTotalAmountnumberTotal amount
offer.linesarrayOffer line items

Each item in offer.lines:

VariableTypeDescription
offer.lines.[index].namestringLine item name
offer.lines.[index].quantitynumberQuantity
offer.lines.[index].payerUnitAmountnumberUnit amount
offer.lines.[index].productIdstringProduct id
offer.lines.[index].externalIdstringExternal product id

Step

VariableTypeDescription
step.idstringCurrent workflow step UUID
step.typestringStep type (e.g. booking_create, custom, dispatching)
step.indexnumberStep position in the workflow

Actor (dictionary by slug)

Only available for actors that have been dispatched (a Dispatching step has completed for that actor). Access actors by their slug.

VariableTypeDescription
actor.[slug].organizationNamestringContractor organization name
actor.[slug].organizationLegalNamestringLegal organization name
actor.[slug].organizationLegalOrgIdstringLegal organization ID (e.g. company registration number)
actor.[slug].organizationResponsibleNamestringFull name of the responsible person
actor.[slug].organizationResponsibleFirstNamestringFirst name
actor.[slug].organizationResponsibleLastNamestringLast name
actor.[slug].organizationResponsibleEmailstringEmail address
actor.[slug].organizationResponsiblePhoneNumberstringPhone number

Replace [slug] with the actor's slug from your workflow (e.g. primary-installer, inspector).

Actors (array)

The actors array contains all workflow actors with their full representative data:

VariableTypeDescription
actors.[index].idstringActor UUID
actors.[index].slugstringActor slug
actors.[index].namestringActor name
actors.[index].activeRepresentative.user.firstNamestringRepresentative first name
actors.[index].activeRepresentative.user.emailstringRepresentative email
actors.[index].activeRepresentative.tenant.namestringOrganization name

Active step actor and representative

VariableTypeDescription
activeStepActor.idstringActor assigned to the current step
activeStepActor.slugstringActor slug
activeStepRepresentative.user.firstNamestringRepresentative's first name
activeStepRepresentative.user.emailstringRepresentative's email
activeStepRepresentative.tenant.namestringOrganization name

activeStepRepresentative is only available if the current step has an assigned actor with an active representative.

Custom fields

Custom fields are available in two formats:

By custom ID, for quick access to the field value:

VariableTypeDescription
customFields.[customId]stringValue of the custom field with the given custom ID

By field UUID, for full field metadata:

VariableTypeDescription
customField.[fieldId].valuestringField value
customField.[fieldId].typestringField type
customField.[fieldId].customIdstringCustom identifier

Custom field values are only available for fields that are required in earlier workflow steps or have default values.

State-dependent variable availability

Variables are populated based on which workflow steps have completed before the current step. If a required step has not yet been reached, the corresponding variables will be empty.

Variable groupRequires
bookingA BookingCreate step exists in the workflow and is connected to the current step or its dependencies
offer / latestOfferAn OfferCreate step has been completed (status = Done)
actor.[slug]The actor has been dispatched (a Dispatching step for that actor is completed)
activeStepRepresentativeThe current step has an assigned actor with an active representative
customFieldsFields are required in earlier steps or have default values

System variables

These variables are always available regardless of which step the webhook is configured on:

VariableDescription
stateThe workflow step state that triggered the webhook
timestampISO 8601 timestamp of when the webhook was triggered
clientAppUrlURL to the client-facing application
routingOperatorAppUrlURL to the routing operator application
contractorAppUrlURL to the contractor application

Retry policy

If your endpoint returns a non-2xx status code or the request fails, Installer.com retries the delivery up to 2 times (3 attempts total) with exponential backoff:

AttemptDelay
1st attemptImmediate
2nd attempt (1st retry)~1 second
3rd attempt (2nd retry)Exponential backoff

After 3 failed attempts, the webhook delivery is marked as failed. You can view the execution logs and status in the workflow step's notification history.

Handling webhooks

Your webhook endpoint should:

  1. Return 2xx quickly: respond within 5 seconds to avoid timeouts
  2. Process asynchronously: queue the event for background processing if it requires heavy work
  3. Be idempotent: the same webhook may fire more than once (e.g. if a step transitions back to a previous state)

Troubleshooting

IssueSolution
Not receiving webhooksVerify the step actually transitions to the configured state. Check the notification history in the workflow step for error logs.
Invalid JSON in payloadEnsure all Handlebars expressions are wrapped in double quotes: "{{order.id}}" not {{order.id}}
Empty variable valuesThe required preceding step may not have been completed. See State-dependent variable availability.
Timeout errorsReturn 200 immediately and process the event asynchronously
Template variable errorA referenced variable is null or undefined. Verify the variable exists for the step's position in the workflow.

On this page