Skip to content

Webhooks

Use incoming webhooks to get real-time updates.

ArcSite uses webhooks to notify your application when an event happens in your organization. Webhooks are particularly useful for asynchronous events like new drawing created.

A webhook enables ArcSite to push real-time notifications to your app. ArcSite uses HTTPS to send these notifications to your app as a JSON payload. You can then use these notifications to execute actions in your backend systems.

You can create and manage your webhooks from the ArcSite web UI.

A of webhook payload contains both event type and payload data.

Event payload
{
"event": "project.created",
"data": {
"id": 36029621652695040,
"name": "project 4",
"created_at": "2022-01-16T04:19:23",
"updated_at": "2022-01-16T04:19:23",
"job_number": "144111",
"customer": {
"name": "Jack",
"phone": "1441",
"second_phone": "1122"
}
}
}
EventDescription
project.createdTriggered when a project is created.
project.updatedTriggered when a project’s own data is modified, such as renaming, descriptions, or tags.
drawing.createdTriggered when a drawing is created by uploading PDF files or uploading a new drawing.
drawing.updatedTriggered when a drawing is manually re-uploaded to the cloud after its initial creation.
proposal.signedTriggered when a proposal is signed in the app. (Deprecated)
proposal.sentTriggered when a proposal is sent to customer. (Deprecated)
proposal.approvedTriggered when a proposal is approved by customer. (Deprecated)
proposal.status_changedTriggered when a proposal status changes.
proposal.payment.receivedTriggered when a payment is successfully received for a proposal.

ArcSite webhooks have built-in retry methods for 3xx, 4xx, or 5xx response status codes. If ArcSite doesn’t quickly receive a 2xx response status code for an event, webhooks will retry the event until it receives a 2xx response or up to certain times.

We highly recommend you secure your integration by ensuring your handler verifies that all webhook request are generated by ArcSite.

The following section describes how to verify webhook signatures:

  • Retrieve your endpoint’s secret.
  • Verify the signature.

Retrieving your endpoint’s secret

Use the ArcSite Webhooks web UI to get the webhook secret. ArcSite generates a unique secret key for each webhook.

Webhhoks

Sample webhook header
{
"ArcSite-Signature": "t=1716975491,v=5f43f28c3d33c34b18634399a8e3bb69dcfb9f146cea562289306d783187d0f1"
}

Verifying the signature

To verify the signature, you need to compare the signature in the header of the incoming webhook request with the signature using the secret and the request body. The ArcSite-Signature header included in each signed event contains a timestamp and signature that you must verify. The timestamp is prefixed by t=, and the signature is prefixed by a v=.

import hmac
import hashlib
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
SECRET = 'your_webhook_secret'
def verify_webhook(header, payload, secret):
# Extract timestamp and signature
elements = dict(item.split('=') for item in header.split(','))
timestamp, signature = elements['t'], elements['v']
# Prepare signed payload string
signed_payload = f"{timestamp}.{payload}"
# Compute expected signature
expected_signature = hmac.new(secret.encode(), signed_payload.encode(), hashlib.sha256).hexdigest()
# Verify signature
return hmac.compare_digest(signature, expected_signature)
# Django Example
@csrf_exempt
def webhook_handler(request):
if request.method == 'POST':
header = request.headers.get('ArcSite-Signature')
payload = request.body.decode('utf-8')
if not verify_webhook(header, payload, SECRET):
return HttpResponse('Invalid signature or timestamp.', status=400)
# TODO Process the valid webhook payload here
return HttpResponse('Webhook received successfully.', status=200)
return HttpResponse('Method not allowed.', status=405)

Step 1: Extract the timestamp and signature from the header

Split the header using the , character as the separator to get a list of elements. Then split each element using the = character as the separator to get a prefix and value pair. The value for the prefix t corresponds to the timestamp and the prefix v to the signature. You can discard all other elements.

Step 2: Prepare the signed_payload string

The signed_payload string is created by concatenating:

  • The timestamp (as a string)
  • The character .
  • The actual JSON payload (that is, the request body)

Step 3: Determine the expected signature

Compute an HMAC with the SHA256 hash function. Use the webhook’s secret as the key, and use the signed_payload string as the message.

Step 4: Compare the signature

Compare the signature in the header to the expected signature.

const express = require("express");
const crypto = require("crypto");
const app = express();
const SECRET = "your_webhook_secret";
app.use(express.raw({ type: "*/*" }));
function verifyWebhook(header, payload, secret) {
// Extract timestamp and signature
const elements = header.split(",").reduce((acc, element) => {
const [key, value] = element.split("=");
acc[key] = value;
return acc;
}, {});
const signature = elements["v"];
// Prepare signed payload string
const signedPayload = `${elements["t"]}.${payload}`;
// Compute expected signature
const expectedSignature = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
// Verify signature
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
);
}
app.post("/webhook", (req, res) => {
const header = req.headers["arcsite-signature"];
const payload = req.body.toString("utf-8");
if (!verifyWebhook(header, payload, SECRET)) {
return res.status(400).send("Invalid signature.");
}
// TODO Process the valid webhook payload here
res.status(200).send("Webhook received successfully.");
});
app.listen(3000, () => {
console.log(`Server is running on port 3000`);
});

project.created Triggered when a project is created.

ParameterTypeDescription
idStringID of the project
nameStringName of the project
customerCustomer[Optional]Customer profile of the project
job_numberString[Optional]Job number of the project
work_site_addressAddress[Optional]Worksite address of the project
sales_repSalesRep[Optional]Sales Representative of the project
tagsList[String]Tags attached to the project

project.updated Triggered when a project’s own data is modified, including:

  • Renaming the project, or updating its description / display name / client profile.
  • Adding or removing a tag on the project, or removing a tag definition that was attached to the project.

The payload schema is identical to project.created.

ParameterTypeDescription
idStringID of the project
nameStringName of the project
customerCustomer[Optional]Customer profile of the project
job_numberString[Optional]Job number of the project
work_site_addressAddress[Optional]Worksite address of the project
sales_repSalesRep[Optional]Sales Representative of the project
tagsList[String]Tags attached to the project

The drawing.created webhook will be triggered when a drawing is created by uploading PDF files on the user site or manually uploading some newly created drawing to the cloud from the ArcSite App.

ParameterTypeDescription
idStringID of the drawing
project_idStringProject ID of the drawing
nameStringName of the drawing
pdf_urlStringDownload address of PDF format of the drawing
png_urlStringDownload address of PNG format of the drawing
tagsList[String]Tags added to this drawing

The drawing.updated webhook is triggered whenever a drawing is manually re-uploaded to the cloud from the ArcSite App after its initial creation.

ParameterTypeDescription
idStringID of the drawing
project_idStringProject ID of the drawing
nameStringName of the drawing
pdf_urlStringDownload address of PDF format of the drawing
png_urlStringDownload address of PNG format of the drawing
tagsList[String]Tags added to this drawing

proposal.signed Triggered when a proposal is signed in the app.

ParameterTypeDescription
project_idStringProject ID of the drawing
nameStringFile name of the signed document
urlStringDownload address of the signed pdf
drawing_idStringThe associated drawing id of the signed pdf
drawing_version_idStringThe associated drawing version id of the signed pdf

proposal.sent Triggered when a proposal is sent to customer.

ParameterTypeDescription
project_ididApproved proposal related project ID
proposal_ididProposal ID
nameStringProposal name
customer_nameStringProposal customer name
contact_emailStringThe sales email
sales_representativeStringThe sales name
proposal_optionsList[ProposalOption]The proposal option data list
ParameterTypeDescription
nameStringProposal option name
drawing_idStringThe proposal option associated drawing ID
drawing_version_idStringThe proposal option associated drawing version ID
totalNumberThe total of the proposal option
pdf_urlStringDownload address of the proposal option pdf file

proposal.approved Triggered when a proposal is approved by customer.

ParameterTypeDescription
project_ididApproved proposal related project ID
proposal_ididProposal ID
nameStringProposal name
customer_nameStringProposal customer name
contact_emailStringThe sales email
sales_representativeStringThe sales name
approved_optionProposalOptionApproved proposal option data

proposal.status_changed Triggered when a proposal status changes.

ParameterTypeDescription
proposal_ididProposal ID
project_ididProposal related project ID
nameStringProposal name
statusStringProposal status (DRAFT/PENDING/VOID/LOST/APPROVED)
sales_representativeStringThe sales name
contact_emailStringThe sales email
customer_nameStringProposal customer name
customer_emailStringProposal customer email
document_numberString(optional) Proposal document number
close_noteString(optional)Note explaining why proposal was closed (Only present when status is VOID or LOST)
totalNumber(optional)The total of the proposal (Only present when status is APPROVED)
pdf_urlString(optional) Download link to the proposal PDF file. Only present when status is APPROVED. Contains the signed version if the proposal has been signed
approved_optionObject(optional) Contains drawing_id (String) field and id (String) field only for online approvals. Only present when status is APPROVED
initial_proposal_idString(optional) ID of the initial proposal. Only present for change order proposals.
proposal_optionsList[ProposalOption](optional) Full list of proposal options for online proposals. Included whenever proposal status changes.
template_idString?The ID of the template used to create the proposal. Returns null when the proposal is signed in app.

proposal.payment.received Triggered when a payment is successfully received for a proposal.

This webhook fires for both ArcSite Payment and Mark as Paid (manual payment recording). The webhook is triggered once per payment transaction.

ArcSite Payment Example
{
"event": "proposal.payment.received",
"data": {
"proposal_id": "12345",
"paid_amount": 5000.0,
"pay_channel": "arcsite_payment",
"paid_time": "2025-11-06T10:30:00+00:00",
"drawing_id": "22444",
"payment_method": "Credit Card"
}
}
Mark as Paid Example (manual payment recording)
{
"event": "proposal.payment.received",
"data": {
"proposal_id": "12345",
"paid_amount": 2500.0,
"pay_channel": "mark_as_paid",
"paid_date": "2025-11-06",
"drawing_id": "22444",
"payment_method": "Check"
}
}
ParameterTypeDescription
proposal_idStringID of the proposal that received the payment
drawing_idStringThe approved drawing id of the proposal
paid_amountNumberAmount of this specific payment (not cumulative). Decimal with 2 decimal places
pay_channelStringPayment channel identifier: arcsite_payment (online payment) or mark_as_paid (manual recording)
paid_timeString(conditional) Timestamp when payment was received. Only present when pay_channel is arcsite_payment. ISO 8601
paid_dateString(conditional) Date when payment was recorded. Only present when pay_channel is mark_as_paid. ISO 8601 date
payment_methodStringPayment method. Examples: Credit Card, ACH, Check, Cash