Live

Real-time events from your workspace

Subscribe once, receive HMAC-signed POSTs for every meaningful change across CRM, sales, billing, banking, stock, HR, projects, and manufacturing.

100+ event types across 40+ resources. At-least-once delivery with exponential retry. Available on Free and every paid plan — register your first endpoint in the dashboard.

Live

Every event below is deliverable today. Register endpoints in Settings → Developer or via POST /v1/webhooks. Every delivery is signed X-Zivvy-Signature: sha256=….

Payload envelope

Every delivery wraps the resource payload in a stable envelope. The envelope shape does not change per event — only the data body does.

POST body
{
  "event": "sales-invoices.paid",
  "resource": "sales-invoices",
  "delivery_id": "whd_2026_07_25_a91f24",
  "timestamp": "2026-07-25T14:02:11.428Z",
  "data": {
    "id": "SINV-0801",
    "customer": "CUST-0012",
    "grand_total": 1250.00,
    "outstanding_amount": 0,
    "paid_at": "2026-07-25T14:02:00Z"
  }
}
Request headers
POST /your/endpoint  HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
User-Agent: Zivvy-Webhooks/1.0
X-Zivvy-Event: sales-invoices.paid
X-Zivvy-Delivery: whd_2026_07_25_a91f24
X-Zivvy-Timestamp: 1753452131
X-Zivvy-Signature: t=1753452131, v1=6f1c...bd94
event
Fully-qualified event name — <resource>.<verb>.
resource
Slug of the resource that triggered the event.
delivery_id
Unique per delivery attempt. Use it for idempotency on your side.
timestamp
ISO 8601, server-side. Included in the signature.
data
The resource payload — same shape as the corresponding GET response.

Event catalog

39+ events across 10 resource groups

Subscribe to specific events, a resource wildcard (sales-orders.*), or all events with *. The machine-readable list is at GET /v1/webhooks/events once outbound delivery is live.

CRM

Pipeline motion — leads and opportunities as they move.

5 events
  • leads.created

    A new lead was captured (form, import, or API).

    Sample payload(show)
    {
      "id": "LEAD-0007",
      "lead_name": "Jane Smith",
      "email_id": "jane@acme.com",
      "status": "Open"
    }
  • leads.updated

    Any field on the lead changed.

    Sample payload(show)
    {
      "id": "LEAD-0007",
      "status": "Contacted"
    }
  • leads.converted

    Lead was converted into a customer + opportunity.

    Sample payload(show)
    {
      "id": "LEAD-0007",
      "customer_id": "CUST-0012",
      "opportunity_id": "OPP-0031"
    }
  • opportunities.won

    An opportunity was marked Won.

    Sample payload(show)
    {
      "id": "OPP-0031",
      "opportunity_amount": 48250,
      "currency": "USD"
    }
  • opportunities.lost

    An opportunity was marked Lost.

    Sample payload(show)
    {
      "id": "OPP-0031",
      "lost_reason": "Price"
    }

Sales

Quotes, orders, and shipments moving through the pipeline.

4 events
  • quotations.submitted

    Quotation was submitted (locked, sent to buyer).

    Sample payload(show)
    {
      "id": "QTN-0102",
      "customer": "CUST-0012",
      "grand_total": 1250.00
    }
  • sales-orders.submitted

    Sales order accepted — start fulfillment.

    Sample payload(show)
    {
      "id": "SO-0451",
      "customer": "CUST-0012",
      "delivery_date": "2026-08-04"
    }
  • sales-orders.cancelled

    Sales order was cancelled after submission.

    Sample payload(show)
    {
      "id": "SO-0451",
      "cancelled_at": "2026-07-25T10:14:00Z"
    }
  • delivery-notes.submitted

    Goods left the warehouse against a sales order.

    Sample payload(show)
    {
      "id": "DN-0223",
      "sales_order": "SO-0451",
      "posting_date": "2026-07-25"
    }

Billing & Invoicing

Invoicing lifecycle — issued, paid, or credited.

5 events
  • sales-invoices.submitted

    Invoice was issued to the customer.

    Sample payload(show)
    {
      "id": "SINV-0801",
      "customer": "CUST-0012",
      "grand_total": 1250.00,
      "due_date": "2026-08-24"
    }
  • sales-invoices.paid

    Invoice was fully settled.

    Sample payload(show)
    {
      "id": "SINV-0801",
      "outstanding_amount": 0,
      "paid_at": "2026-08-11T14:02:00Z"
    }
  • purchase-invoices.submitted

    Supplier bill was recorded.

    Sample payload(show)
    {
      "id": "PINV-0334",
      "supplier": "SUP-0004"
    }
  • payment-entries.submitted

    A payment was posted against one or more invoices.

    Sample payload(show)
    {
      "id": "PE-0912",
      "paid_amount": 1250.00,
      "references": [{ "sales_invoice": "SINV-0801" }]
    }
  • payment-requests.paid

    A hosted payment request was completed by the payer.

    Sample payload(show)
    {
      "id": "PR-0087",
      "gateway": "stripe",
      "amount": 1250.00
    }

Purchasing

Purchase orders and inbound receipts.

2 events
  • purchase-orders.submitted

    PO was issued to the supplier.

    Sample payload(show)
    {
      "id": "PO-0119",
      "supplier": "SUP-0004"
    }
  • purchase-receipts.submitted

    Goods received against a PO — stock has increased.

    Sample payload(show)
    {
      "id": "PREC-0281",
      "purchase_order": "PO-0119"
    }

Stock

Movement, transfers, and inventory events.

4 events
  • items.created

    A new item was added to the catalog.

    Sample payload(show)
    {
      "id": "ITM-0455",
      "item_code": "WIDGET-BLK-L",
      "item_group": "Widgets"
    }
  • stock-entries.submitted

    Stock was moved, issued, or received internally.

    Sample payload(show)
    {
      "id": "SE-0621",
      "stock_entry_type": "Material Transfer"
    }
  • pick-lists.submitted

    A pick list was generated for warehouse fulfillment.

    Sample payload(show)
    {
      "id": "PL-0034",
      "purpose": "Delivery"
    }
  • shipments.submitted

    A shipment was booked with a carrier.

    Sample payload(show)
    {
      "id": "SH-0088",
      "delivery_note": "DN-0223"
    }

Banking

Bank feeds, reconciliation, and balance changes.

5 events
  • bank-transactions.created

    A new bank transaction landed in a feed.

    Sample payload(show)
    {
      "id": "BT-2026-0000451",
      "bank_account": "BA-DE-DKB-01",
      "amount": 1250.00,
      "date": "2026-07-25"
    }
  • bank-transactions.matched

    A bank transaction was matched to a payment or invoice.

    Sample payload(show)
    {
      "id": "BT-2026-0000451",
      "matched_to": "PE-0912"
    }
  • bank-transactions.reconciled

    A bank transaction was reconciled and closed.

    Sample payload(show)
    {
      "id": "BT-2026-0000451"
    }
  • bank-accounts.balance_updated

    The running balance on a bank account changed.

    Sample payload(show)
    {
      "id": "BA-DE-DKB-01",
      "balance": 48250.00,
      "currency": "EUR"
    }
  • bank-statement-imports.completed

    A statement file finished importing.

    Sample payload(show)
    {
      "id": "BSI-0011",
      "imported_rows": 84
    }

HR & People

Joiners, leavers, leave, expenses, and payroll.

5 events
  • employees.created

    A new employee record was added.

    Sample payload(show)
    {
      "id": "EMP-0033",
      "employee_name": "Priya Patel"
    }
  • leave-applications.approved

    A leave request was approved.

    Sample payload(show)
    {
      "id": "LA-0142",
      "employee": "EMP-0033",
      "from_date": "2026-08-05",
      "to_date": "2026-08-09"
    }
  • attendance.submitted

    Attendance was recorded for a day.

    Sample payload(show)
    {
      "id": "ATT-2026-07-25-0033",
      "status": "Present"
    }
  • expense-claims.approved

    An expense claim was approved for reimbursement.

    Sample payload(show)
    {
      "id": "EC-0077",
      "total_claimed_amount": 142.50
    }
  • salary-slips.submitted

    A payroll slip was finalized for the period.

    Sample payload(show)
    {
      "id": "SAL-2026-07-EMP-0033",
      "net_pay": 3820.00
    }

Projects

Projects, tasks, and time tracking.

3 events
  • projects.completed

    A project was marked complete.

    Sample payload(show)
    {
      "id": "PROJ-0018",
      "project_name": "Website refresh"
    }
  • tasks.completed

    A task moved to Completed.

    Sample payload(show)
    {
      "id": "TASK-0221",
      "project": "PROJ-0018"
    }
  • timesheets.submitted

    A timesheet was submitted for approval.

    Sample payload(show)
    {
      "id": "TS-0089",
      "total_hours": 37.5
    }

Manufacturing

Production plans, work orders, and BOMs.

3 events
  • boms.submitted

    A BOM was submitted (active).

    Sample payload(show)
    {
      "id": "BOM-WIDGET-001",
      "item": "ITM-0455"
    }
  • work-orders.completed

    A work order finished production.

    Sample payload(show)
    {
      "id": "WO-0154",
      "produced_qty": 500
    }
  • production-plans.completed

    A production plan closed out.

    Sample payload(show)
    {
      "id": "PP-0022"
    }

Support & Assets

Tickets and asset lifecycle.

3 events
  • issues.created

    A new support ticket was opened.

    Sample payload(show)
    {
      "id": "ISS-0409",
      "customer": "CUST-0012",
      "priority": "High"
    }
  • issues.resolved

    A ticket was marked Resolved.

    Sample payload(show)
    {
      "id": "ISS-0409",
      "resolution_time_seconds": 18420
    }
  • assets.scrapped

    An asset was scrapped from the register.

    Sample payload(show)
    {
      "id": "ASSET-0044",
      "scrap_date": "2026-07-25"
    }

Signature verification

Every delivery is signed with an HMAC-SHA256 of <timestamp>.<raw-body> using the shared secret you supplied on the subscription. The signature travels in the X-Zivvy-Signature header. Verify against the raw request body — do not parse JSON first.

import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.ZIVVY_WEBHOOK_SECRET!;

// Zivvy signs the raw body. Use express.raw() so req.body stays
// as a Buffer — do NOT run JSON parsing before verification.
app.post(
  "/webhooks/zivvy",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signatureHeader = req.header("X-Zivvy-Signature") || "";
    const timestamp = req.header("X-Zivvy-Timestamp") || "";

    // Reject payloads older than 5 minutes to blunt replay attacks.
    const age = Math.abs(Date.now() / 1000 - Number(timestamp));
    if (!Number.isFinite(age) || age > 300) {
      return res.status(400).send("stale timestamp");
    }

    const payload = `${timestamp}.${req.body.toString("utf8")}`;
    const expected = crypto
      .createHmac("sha256", SECRET)
      .update(payload)
      .digest("hex");

    // "t=…, v1=<hex>" — accept any v1 sig in the header.
    const v1 = /v1=([a-f0-9]+)/.exec(signatureHeader)?.[1] ?? "";

    const ok =
      v1.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));

    if (!ok) return res.status(400).send("bad signature");

    const event = JSON.parse(req.body.toString("utf8"));
    // Ack fast (2xx within 5s), then process in a background job.
    res.status(200).send("ok");
  }
);

Delivery guarantees

At-least-once delivery

Every event is delivered at least once. Design idempotently — de-duplicate on delivery_id, which is stable across retries.

Exponential backoff

Non-2xx or timeout (>10s) triggers a retry with jittered exponential backoff — up to 24 hours, 12 attempts.

Ordering is not guaranteed

Two events can arrive out of order. Trust the timestamp on the envelope, not the arrival order.

Delivery log

Every attempt — status code, latency, response body preview, and error — is retained for 30 days. Fetch it any time to debug:

GET
curl https://api.zivvy.xyz/v1/webhooks/whk_01H8.../deliveries \
  -H "Authorization: Bearer $ZIVVY_API_KEY"

Register a webhook

Management endpoints (POST /v1/webhooks, GET /v1/webhooks, DELETE /v1/webhooks/:id) are already live. Registrations you make today will start receiving events automatically when outbound delivery ships — no re-registration needed.

POST
curl -X POST https://api.zivvy.xyz/v1/webhooks \
  -H "Authorization: Bearer $ZIVVY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/webhooks/zivvy",
    "events": [
      "sales-invoices.paid",
      "sales-orders.submitted",
      "bank-transactions.matched"
    ],
    "secret": "whsec_your_generated_shared_secret",
    "label": "Production billing sync"
  }'

IP allowlist · pending

A stable set of egress IPs will be published at launch so you can pin firewall rules. Until then, rely on the HMAC signature as the primary authenticity check — signature verification is enough on its own, and remains the recommended approach even once IPs are pinned.

Ship your first webhook in under a minute

Register an endpoint, subscribe to the events you care about, and get signed POSTs the moment something happens in your workspace.