Webhooks
In this guide, we will look at how to register and consume webhooks to integrate your app with Forms Live. With webhooks, your app can know when something happens in Forms Live, such as when a form is finalised, signed, or when a user template is updated.
Registering webhooks
To register a new webhook, you need to have a URL in your app that Forms Live can call. You can configure a new webhook via the API, specifying the events you want to listen for, and providing your URL.
The webhook system will attempt to POST to your supplied URI up to 3 times before giving up. Your webhook URI must respond with a 200 OK status within 5 seconds to successfully acknowledge the webhook.
Example webhook creation
{
"active": true,
"events": ["form.finalise", "form.sign"],
"uri": "https://webhook.yourdomain.com/endpoint",
"form_id": null
}
Now, whenever something of interest happens in your app, a webhook is fired off by Forms Live. In the next section, we'll look at how to manage webhooks.
Managing webhooks
Forms Live provides a comprehensive set of endpoints to manage your webhooks:
- List all webhooks: Retrieve all webhooks configured for your account
- Get a single webhook: Retrieve details about a specific webhook
- Create a webhook: Register a new webhook with your desired events and URL
- Update a webhook: Modify an existing webhook's configuration
- Delete a webhook: Remove a webhook when it's no longer needed
Validating a webhook
Every webhook request from Forms Live is signed so you can confirm it came from us and was not altered in transit. The signature is sent in the Formslive-Signature header in the following format:
Formslive-Signature header
Formslive-Signature: t=1725840000000,sha256=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
tis the time the signature was generated, in milliseconds since the Unix epoch.sha256is a hex-encoded HMAC-SHA256 digest.
The signing secret is your API key, the same key used to build your Authorization header (see Authentication). Keep it on your server and never expose it in client-side code.
To validate a request:
- Read the
Formslive-Signatureheader and extract thetandsha256values. - Take the raw request body exactly as received, before parsing it as JSON. Re-serialising the body will change the bytes and the signature will not match.
- Build the signed string by joining the timestamp and the raw body with a full stop:
{t}.{raw body}. - Compute the HMAC-SHA256 of the signed string using your API key as the secret, and hex-encode the result.
- Compare your digest to the
sha256value using a constant-time comparison. If they differ, reject the request. - Check that
tis within an acceptable tolerance of the current time to guard against replay. We recommend five minutes. Retried deliveries reuse the original signature and timestamp, so a tolerance of only a few seconds will reject legitimate retries.
If the signature and timestamp are both valid, the request is authentic. The id in the request body uniquely identifies the delivery and is the same across retries, so use it to de-duplicate events you have already processed.
Event types
- Name
form.create- Description
A new form was created.
- Name
form.finalise- Description
A form was finalised.
- Name
form.sign- Description
A form was signed.
- Name
form.executed- Description
The was signed by the last signer.
- Name
form.update- Description
A form was updated.
- Name
form.delete- Description
A form was successfully deleted.
- Name
user_template.create- Description
A new user template was created.
- Name
user_template.update- Description
An existing user template was updated.
- Name
user_template.delete- Description
A user template was successfully deleted.
Example payload
{
"id": "7a8b9c0d1e2f",
"type": "form.finalise",
"payload": {
"id": 123,
"template_version_id": 456,
"agency_id": 789,
"user_id": 101,
"name": "Property Lease Agreement",
"template": false,
"finalised": true,
"private": false,
"created": 1506294669,
"updated": 1506294769,
"given_name": "John",
"surname": "Smith",
"template_cost": 80,
"template_id": 202,
"template_name": "Residential Lease Agreement",
"template_code": "RLA2023",
"template_instruction_pages": 1
}
}
Webhook endpoints
List all webhooks
This endpoint allows you to retrieve a list of all webhooks configured for your account.
Request
curl https://app-api.formslive.com.au/webhooks/ \
--request GET \
--header "Authorization: Basic YTlkOWIwYItNGY4Yi1hYTQxLTI5NzZmMTcyZmEyMQ=="
Response
[
{
"id": 1,
"active": true,
"events": ["form.finalise", "form.sign"],
"uri": "https://webhook.yourdomain.com/endpoint",
"form_id": null
},
{
"id": 2,
// ...
}
]
Get a single webhook
This endpoint allows you to retrieve a specific webhook by providing its ID.
Request
curl https://app-api.formslive.com.au/webhooks/1 \
--request GET \
--header "Authorization: Basic YTlkOWIwYItNGY4Yi1hYTQxLTI5NzZmMTcyZmEyMQ=="
Response
{
"id": 1,
"active": true,
"events": ["form.finalise", "form.sign"],
"uri": "https://webhook.yourdomain.com/endpoint",
"form_id": null
}
Create a webhook
This endpoint allows you to register a new webhook for specific events.
Required attributes
- Name
events- Type
- array
- Description
Array of event types to listen for.
- Name
uri- Type
- string
- Description
The URL that Forms Live will POST webhook data to.
Optional attributes
- Name
active- Type
- boolean
- Description
Whether the webhook should be active. Default is true.
- Name
form_id- Type
- integer
- Description
If the webhook is for a specific form, specify the form ID.
- Name
user_template_id- Type
- integer
- Description
If the webhook is for a specific user template, specify the user template ID.
Request
curl https://app-api.formslive.com.au/webhooks/ \
--request POST \
--header "content-type: application/json" \
--header "Authorization: Basic YTlkOWIwYItNGY4Yi1hYTQxLTI5NzZmMTcyZmEyMQ==" \
--data '{"active": true, "events": ["form.finalise"], "uri": "https://webhook.yourdomain.com/endpoint"}'
Response
{
"id": 1,
"active": true,
"events": ["form.finalise"],
"uri": "https://webhook.yourdomain.com/endpoint",
"form_id": null
}
Update a webhook
This endpoint allows you to update an existing webhook's configuration.
Optional attributes
- Name
events- Type
- array
- Description
Array of event types to listen for.
- Name
uri- Type
- string
- Description
The URL that Forms Live will POST webhook data to.
- Name
active- Type
- boolean
- Description
Whether the webhook should be active.
- Name
form_id- Type
- integer
- Description
If the webhook is for a specific form, specify the form ID.
- Name
user_template_id- Type
- integer
- Description
If the webhook is for a specific user template, specify the user template ID.
Request
curl https://app-api.formslive.com.au/webhooks/1 \
--request PUT \
--header "content-type: application/json" \
--header "Authorization: Basic YTlkOWIwYItNGY4Yi1hYTQxLTI5NzZmMTcyZmEyMQ==" \
--data '{"active": false, "events": ["form.finalise", "form.sign"]}'
Response
{
"id": 1,
"active": false,
"events": ["form.finalise", "form.sign"],
"uri": "https://webhook.yourdomain.com/endpoint",
"form_id": null
}
Delete a webhook
This endpoint allows you to delete a webhook when it's no longer needed.
Request
curl https://app-api.formslive.com.au/webhooks/1 \
--request DELETE \
--header "Authorization: Basic YTlkOWIwYItNGY4Yi1hYTQxLTI5NzZmMTcyZmEyMQ=="
Response
{
"message": "The webhook was successfully deleted."
}
Reliability and best practices
When implementing webhooks with Forms Live, consider the following best practices:
- Quick responses: Your webhook endpoint should respond with a 200 OK status quickly (within 5 seconds) to acknowledge receipt
- Idempotency: Design your webhook handler to be idempotent, as the same webhook might be delivered multiple times in case of retries
- Validation: Verify the
Formslive-Signatureheader on every request, as described in Validating a webhook - Async processing: Acknowledge the webhook quickly, then process the data asynchronously if needed
- Logging: Maintain logs of received webhooks for debugging purposes
By following these best practices, you'll ensure a robust integration with Forms Live's webhook system.