Overview
DoorStax dispatches webhooks for key events from integrated payment and screening providers. Webhooks are sent as HTTP POST requests with JSON payloads to your configured endpoint.
Kadima Payment Events
Payment events are dispatched by the Kadima payment processor and forwarded to your webhook endpoint.
| Event | Description |
|---|---|
| transaction.completed | A payment has been successfully processed |
| transaction.failed | A payment attempt has failed |
| transaction.refunded | A payment has been refunded to the payer |
| recurring.processed | A recurring payment has been automatically processed |
Signature Verification
All webhook payloads are signed using HMAC-SHA256. The signature is sent in the x-kadima-signature header. Always verify signatures before processing webhooks.
import crypto from "crypto";
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post("/webhooks/kadima", (req, res) => {
const signature = req.headers["x-kadima-signature"];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.KADIMA_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).json({ error: "Invalid signature" });
}
// Process the webhook event
const { event, data } = req.body;
// ...
res.status(200).json({ received: true });
});Example Payloads
transaction.completed
{
"event": "transaction.completed",
"timestamp": "2025-01-15T14:30:00.000Z",
"data": {
"transactionId": "txn_abc123",
"amount": 150000,
"currency": "USD",
"status": "completed",
"paymentMethod": "ach",
"tenant": {
"id": "tnt_012",
"email": "tenant@example.com"
},
"lease": {
"id": "lea_xyz789",
"unitId": "unt_456"
},
"metadata": {
"description": "Rent payment - January 2025"
}
}
}transaction.failed
{
"event": "transaction.failed",
"timestamp": "2025-01-15T14:30:00.000Z",
"data": {
"transactionId": "txn_def456",
"amount": 150000,
"currency": "USD",
"status": "failed",
"failureReason": "insufficient_funds",
"failureCode": "R01",
"paymentMethod": "ach",
"tenant": {
"id": "tnt_012",
"email": "tenant@example.com"
},
"retryEligible": true,
"nextRetryAt": "2025-01-18T14:30:00.000Z"
}
}transaction.refunded
{
"event": "transaction.refunded",
"timestamp": "2025-01-20T10:00:00.000Z",
"data": {
"transactionId": "txn_abc123",
"refundId": "ref_ghi789",
"amount": 150000,
"currency": "USD",
"status": "refunded",
"reason": "duplicate_payment",
"initiatedBy": "usr_pm001"
}
}recurring.processed
{
"event": "recurring.processed",
"timestamp": "2025-02-01T06:00:00.000Z",
"data": {
"transactionId": "txn_rec001",
"recurringId": "rec_abc123",
"amount": 150000,
"currency": "USD",
"status": "completed",
"paymentMethod": "ach",
"schedule": {
"frequency": "monthly",
"dayOfMonth": 1,
"nextDate": "2025-03-01"
},
"tenant": {
"id": "tnt_012",
"email": "tenant@example.com"
}
}
}RentSpree Screening Webhooks
DoorStax integrates with RentSpree for tenant background screening. Screening results are delivered via webhooks when the screening is complete.
| Event | Description |
|---|---|
| screening.completed | Screening report is ready for review |
| screening.pending | Screening has been initiated and is in progress |
| screening.failed | Screening could not be completed |
Retry Policy
If your endpoint returns a non-2xx status code or times out (30 seconds), DoorStax will retry the webhook delivery with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry (final) | 24 hours |
Best Practice: Always return a 200 status code quickly and process the webhook asynchronously. This prevents timeouts and ensures reliable delivery.