Skip to content

Webhooks

Webhooks send HTTP POST requests to your server when events occur, so you don’t need to poll the API for changes.

Quick Start

Create a webhooktypescript
const webhook = await sdk.webhooks.create({
  url: "https://example.com/webhooks",
  events: ["invoice.created", "invoice.paid", "payment.received"],
  description: "Invoice notifications",
});

// Save the secret — it is only shown on creation
console.log("Webhook secret:", webhook.secret);

Event Types

Subscribe to specific events or listen to all events for a category.

Invoices

EventTrigger
invoice.createdInvoice created
invoice.updatedInvoice updated
invoice.sentInvoice sent to customer
invoice.paidInvoice fully paid
invoice.overdueInvoice past due date
invoice.cancelledInvoice cancelled
invoice.deletedInvoice soft-deleted
invoice.restoredInvoice restored from deletion
invoice.voidedInvoice voided
invoice.finalizedInvoice finalized (locked)

Customers

EventTrigger
customer.createdCustomer created
customer.updatedCustomer updated
customer.deletedCustomer soft-deleted
customer.restoredCustomer restored
customer.permanently_deletedCustomer permanently removed

Payments

EventTrigger
payment.receivedPayment received
payment.failedPayment failed
payment.deletedPayment soft-deleted
payment.restoredPayment restored
payment.permanently_deletedPayment permanently removed

Estimates

EventTrigger
estimate.createdEstimate created
estimate.sentEstimate sent
estimate.acceptedEstimate accepted by customer
estimate.rejectedEstimate rejected
estimate.deletedEstimate soft-deleted
estimate.restoredEstimate restored

Credit Notes

EventTrigger
credit_note.createdCredit note created
credit_note.issuedCredit note issued
credit_note.voidedCredit note voided
credit_note.deletedCredit note soft-deleted
credit_note.restoredCredit note restored

Advance Invoices

EventTrigger
advance_invoice.createdAdvance invoice created
advance_invoice.paidAdvance invoice paid
advance_invoice.appliedAdvance applied to final invoice
advance_invoice.voidedAdvance invoice voided
advance_invoice.deletedAdvance invoice soft-deleted
advance_invoice.restoredAdvance invoice restored

Recurring Invoices

EventTrigger
recurring_invoice.createdSchedule created
recurring_invoice.updatedSchedule updated
recurring_invoice.deletedSchedule soft-deleted
recurring_invoice.restoredSchedule restored
recurring_invoice.permanently_deletedSchedule permanently removed
recurring_invoice.pausedSchedule paused
recurring_invoice.resumedSchedule resumed
recurring_invoice.invoice_generatedInvoice generated from schedule
recurring_invoice.generation_failedInvoice generation failed
recurring_invoice.completedAll invoices generated

Items & Taxes

EventTrigger
item.createdItem created
item.deletedItem soft-deleted
item.restoredItem restored
item.permanently_deletedItem permanently removed
tax.createdTax created
tax.updatedTax updated
tax.deletedTax soft-deleted
tax.restoredTax restored
tax.permanently_deletedTax permanently removed

Orders & Delivery Notes

EventTrigger
order.createdOrder created
order.updatedOrder updated
order.deletedOrder soft-deleted
order.restoredOrder restored
order.permanently_deletedOrder permanently removed
order.processedOrder processed
order.cancelledOrder cancelled
order.failedOrder processing failed
order_integration.createdOrder integration created
order_integration.updatedOrder integration updated
order_integration.deletedOrder integration deleted
delivery_note.createdDelivery note created
delivery_note.sentDelivery note sent
delivery_note.cancelledDelivery note cancelled
delivery_note.voidedDelivery note voided
delivery_note.deletedDelivery note soft-deleted
delivery_note.restoredDelivery note restored

E-Invoicing & Integrations

EventTrigger
e_invoicing.submission.createdE-invoice submitted
e_invoicing.submission.deliveredE-invoice delivered
e_invoicing.submission.failedE-invoice delivery failed
e_invoicing.supplier.onboardedSupplier onboarded for e-invoicing
e_invoicing.supplier.rejectedSupplier onboarding rejected
stripe_app.connectedStripe app connected
stripe_app.disconnectedStripe app disconnected
stripe_app.settings_updatedStripe app settings changed

Payload Format

Every webhook delivery sends a JSON payload:

{
"event": "invoice.paid",
"data": {
"id": "inv_abc123",
"number": "INV-001",
"..."
},
"timestamp": "2024-01-15T10:30:00.000Z"
}

The data field contains the full resource object at the time of the event.

Delivery Headers

HeaderDescription
X-Webhook-SignatureHMAC-SHA256 signature (sha256=<hex>)
X-Webhook-TimestampUnix timestamp (seconds)
X-Webhook-EventEvent type
X-Webhook-DeliveryUnique delivery ID
X-Webhook-IdWebhook ID
Content-Typeapplication/json

Verifying Signatures

Every delivery is signed with your webhook secret using HMAC-SHA256. Always verify signatures to ensure requests are from Space Invoices.

Verify webhook signaturetypescript
import crypto from "node:crypto";

function verifyWebhookSignature(
  secret: string,
  payload: string,
  signatureHeader: string,
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return signatureHeader === `sha256=${expected}`;
}

// In your webhook handler:
const rawBody = await request.text();
const signature = request.headers.get("X-Webhook-Signature")!;

if (!verifyWebhookSignature("your_webhook_secret", rawBody, signature)) {
  throw new Error("Invalid webhook signature");
}

Handling Events

Process events asynchronously and respond quickly with a 2xx status.

Handle webhook eventstypescript
app.post("/webhooks", async (req, res) => {
  const { event, data, timestamp } = req.body;

  switch(event) {
    case "invoice.paid":
      await fulfillOrder(data.id);
      break;
    case "invoice.overdue":
      await sendReminder(data.id);
      break;
    case "customer.created":
      await syncToExternalCRM(data);
      break;
  }

  // Always respond 2xx quickly
  res.status(200).json({ received: true });
});

Retry Policy

Failed deliveries (non-2xx response or timeout) are retried up to 5 times with increasing delays:

AttemptDelay
11 minute
25 minutes
330 minutes
42 hours
524 hours

Deliveries that return 410 Gone are not retried and the webhook is automatically deactivated.

The delivery timeout is 10 seconds — your endpoint must respond within this window.

Best Practices

  1. Respond 2xx quickly — Do heavy processing asynchronously after acknowledging the webhook
  2. Verify signatures — Always validate X-Webhook-Signature before processing
  3. Handle duplicates — Use the X-Webhook-Delivery header as an idempotency key in case of retries
  4. Monitor deliveries — Check webhook delivery logs in the dashboard for failures
  5. Use specific events — Subscribe only to events you need rather than all events
  6. Secure your endpoint — Use HTTPS and restrict access to Space Invoices IP ranges if possible