Skip to content

Subscriptions

Palmetto offers a suite of webhooks that will allow you to be alerted to status changes to your projects automatically.

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.

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:

TriggerDescription
PROJECT_CREATEDany project created
PROJECT_SUBMISSION_CREATEDany project submission created
PROJECT_SUBMISSION_UPDATEDany project submission updated
PROJECT_STAGE_COMPLETEDany project stage completed
PROJECT_STATUS_UPDATEDany project status updated
PROJECT_CHANGE_ORDER_OPENEDany project change order opened
PROJECT_CHANGE_ORDER_UPDATEDany 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.

  • 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.

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"
}
}