Subscriptions
Palmetto offers a suite of webhooks that will allow you to be alerted to status changes to your projects automatically.
Prerequisites
Section titled “Prerequisites”In order to register a webhook, you’ll need to have an endpoint available with which to register. This is what the webhook will call when the event occurs. You will also need to have a registered account with which to authenticate your requests. For more information, refer to the getting started docs.
Registering your webhooks
Section titled “Registering your webhooks”The subscriptions endpoints are used to manage and register webhooks. In order to register your endpoint to receive events, we’ll be using the POST:/api/subscriptions/webhooks endpoint. The request body for this endpoint requires at minimum the url for your endpoint and the HTTP verb. You can also register the triggers here ahead of time.
import fetch from 'node-fetch';
const response = await fetch(`${envBaseUrl}/api/subscriptions/webhooks`, { method: 'POST', body: JSON.stringify({ url: "https://3823-50-217-78-126.ngrok.io/webhook-test", method: "POST", triggers: [] }) headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` }});const webhookResponse = await response.json();You should receive a response back confirming the details you submitted along with the status of the webhook and its unique identifier.
{ "status": "ACTIVE", "triggers": [], "id": "64alz9c4372c6ddcf2052222", "method": "POST", "url": "https://3823-50-217-78-126.ngrok.io/webhook-test"}Now that you’ve established your webhook, you can add the events you wish to be alerted on to it as triggers. We’ll use the POST:api/subscriptions/webhooks/id/triggers/event endpoint for this. There are numerous events you can subscribe to. For this example, we’ll listen for the PROJECT_CREATED event.
import fetch from 'node-fetch';
const response = await fetch( `${envBaseUrl}/api/subscriptions/webhooks/64alz9c4372c6ddcf2052222/triggers/PROJECT_CREATED`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` }, },);
const webhookResponse = await response.json();You’ll receive a response back with the updated state of your webhook.
{ "status": "ACTIVE", "triggers": ["PROJECT_CREATED"], "id": "64alz9c4372c6ddcf2052222", "method": "POST", "url": "https://3823-50-217-78-126.ngrok.io/webhook-test"}You can add as many triggers as you wish. The following triggers are supported:
| Trigger | Description |
|---|---|
PROJECT_CREATED | any project created |
PROJECT_SUBMISSION_CREATED | any project submission created |
PROJECT_SUBMISSION_UPDATED | any project submission updated |
PROJECT_STAGE_COMPLETED | any project stage completed |
PROJECT_STATUS_UPDATED | any project status updated |
PROJECT_CHANGE_ORDER_OPENED | any project change order opened |
PROJECT_CHANGE_ORDER_UPDATED | any project change order updated |
You can also remove triggers you no longer wish to receive with the DELETE:api/subscriptions/webhooks/id/triggers/event endpoint.
import fetch from 'node-fetch';
const response = await fetch( `${envBaseUrl}/api/subscriptions/webhooks/64alz9c4372c6ddcf2052222/triggers/PROJECT_CREATED`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` }, },);
const webhookResponse = await response.json();{ "status": "ACTIVE", "triggers": [], "id": "64alz9c4372c6ddcf2052222", "method": "POST", "url": "https://3823-50-217-78-126.ngrok.io/api/v2/bookings/webhook-test"}Now that you have a webhook set up, you will begin to receive the events that you registered to that endpoint. This is an example payload:
{ "event": "PROJECT_CREATED", "data": { "id": "64ccfad1372c6ddcf205b5bc" }}Each event may have unique data related to it as part of the payload, but every payload will have the event property and the unique id property.
Considerations
Section titled “Considerations”- There are several ways you can set up your webhooks. You can have all events you care about be registered to the same endpoint, you can have an individual endpoint per event, or some combination of the two. Pick whichever method best suits your workflow.
- You can use a tool like ngrok or webhook.site in order to assist in testing your webhooks.
Testing
Section titled “Testing”In order to ease the testing of subscriptions, we offer a test endpoint to allow for manual triggers of the various events. The test endpoint accepts a subscriptionId and the event you wish to trigger. The payload requires a projectId as well.
import fetch from 'node-fetch';
const event = 'PROJECT_CREATED';const subscriptionId = '64alz9c4372c6ddcf2052222';const response = await fetch(`${envBaseUrl}/api/subscriptions/webhooks/${subscriptionId}/triggers/${event}/test`, { method: 'POST', body: JSON.stringify({ projectId: '64alz9c3372c6decf2342222', }), headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` },});If the request was successful, you should see a request made to the webhook you registered with the given subscription id.
For the PROJECT_STAGE_COMPLETED and PROJECT_STATUS_UPDATED webhooks, you can provide an optional stage or status property to pass along on the webhook response. If omitted from the payload, a default will be used.
import fetch from 'node-fetch';
const event = 'PROJECT_STAGE_COMPLETED';const subscriptionId = '64alz9c4372c6ddcf2052222';const response = await fetch(`${envBaseUrl}/api/subscriptions/webhooks/${subscriptionId}/triggers/${event}/test`, { method: 'POST', body: JSON.stringify({ projectId: '64alz9c3372c6decf2342222', stage: 'submission', }), headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` },});{ "event": "PROJECT_STAGE_COMPLETED", "data": { "stage": "submission", "completedAt": "2023-08-31T14:27:58.159Z" }}import fetch from 'node-fetch';
const event = 'PROJECT_STATUS_UPDATED';const subscriptionId = '64alz9c4372c6ddcf2052222';const response = await fetch(`${envBaseUrl}/api/subscriptions/webhooks/${subscriptionId}/triggers/${event}/test`, { method: 'POST', body: JSON.stringify({ projectId: '64alz9c3372c6decf2342222', status: 'QUALIFYING', }), headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` },});{ "event": "PROJECT_STATUS_UPDATED", "data": { "oldStatus": "DRAFT", "newStatus": "QUALIFYING" }}