Zoye LogoZoye Logo
עוזר AIתמחוראודותDiscord

Get started

  • Overview
  • Quickstart

Connect

  • No-code embed
  • REST API
  • MCP server

Outbound

  • Webhooks

Reference

  • Rate limits
  • Field reference
  • FAQ
Open the Sync Users & Subscribers connector

Outbound

Webhooks

Webhooks are the outbound half of the Sync Users & Subscribers connector. Instead of pushing contacts into Zoye, they let Zoye notify your own server the moment a contact is created, updated, changes stage, or is archived - so your systems stay in sync in real time.

How it works

  1. You register an endpoint URL on your server and choose which events to receive.
  2. When a matching event happens in Zoye, we POST a signed JSON payload to that URL.
  3. Your server verifies the signature, does its work, and responds with a 2xx status to acknowledge.
  4. If your server is down or returns a non-2xx, we retry with exponential backoff.

Set it up

Webhooks are managed in the app, not with an API key. Open the Sync Users & Subscribers connector, choose Send updates out, then:

  1. Add the endpoint URL on your server that should receive events.
  2. Pick the events you care about (or all of them).
  3. Copy the signing secret (whsec_...) shown once on creation, and store it on your server. You use it to verify every delivery.

One secret per endpoint

Each endpoint gets its own signing secret, shown only once when you create it. If you lose it, delete the endpoint and add a new one. You can expand any endpoint in the app to see recent deliveries and replay failed ones.

Events

EventFires when
contact.createdA new contact (person or company) is added.
contact.updatedAny field on a contact changes.
contact.stage_changedA contact moves to a different lifecycle stage. Includes previousStage.
contact.archivedA contact is archived (soft-deleted).
contact.restoredAn archived contact is restored.
contact.deletedA contact is permanently deleted.

Payload

Every delivery is a JSON object with the event name, an ISO 8601 timestamp, the workspace id, and a data object with the contact. The data shape matches the contact you get back from the REST API.

Example: contact.created
JSON
{
  "event": "contact.created",
  "timestamp": "2026-06-05T12:34:56.000Z",
  "workspace_id": 1624,
  "data": {
    "id": 1054,
    "email": "jordan@northwind.com",
    "name": "Jordan Rivera",
    "phone": "+15551234567",
    "company": "Northwind",
    "position": "CTO",
    "tags": ["vip"],
    "source": "website-form",
    "lifecycleStage": "Subscriber",
    "archivedAt": null,
    "externalId": null,
    "createdAt": "2026-06-05T12:34:56.000Z",
    "updatedAt": "2026-06-05T12:34:56.000Z"
  }
}

For contact.stage_changed, the data object also includes previousStage so you can see where the contact moved from:

Example: contact.stage_changed (data only)
JSON
"data": {
  "id": 1054,
  "name": "Jordan Rivera",
  "lifecycleStage": "Customer",
  "previousStage": "Lead"
  // ...the rest of the contact fields
}

Request headers

HeaderMeaning
X-Zoye-SignatureHMAC-SHA256 of the raw request body, prefixed with sha256=. Verify this.
X-Zoye-EventThe event type, e.g. contact.created.
X-Zoye-DeliveryA unique id for this delivery attempt. Use it for idempotency.
User-AgentAlways Zoye-Webhooks/1.0.

Verify the signature

Always verify the signature before trusting a payload. Compute the HMAC-SHA256 of the raw request body with your endpoint secret, prefix it with sha256=, and compare it to the X-Zoye-Signature header using a constant-time comparison. Use the raw bytes of the body, not a re-serialized object.

Read the raw body

Frameworks that auto-parse JSON can change the bytes (key order, whitespace) and break the signature. Capture the raw body before parsing - for example Express needs express.raw({ type: "application/json" }) on the webhook route.

const crypto = require("crypto");
const express = require("express");
const app = express();

const SECRET = process.env.ZOYE_WEBHOOK_SECRET; // whsec_...

app.post(
  "/zoye-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-Zoye-Signature") || "";
    const expected =
      "sha256=" +
      crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");

    const a = Buffer.from(signature);
    const b = Buffer.from(expected);
    if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
      return res.status(401).send("bad signature");
    }

    const payload = JSON.parse(req.body.toString("utf8"));
    // ...handle payload.event + payload.data...
    res.sendStatus(200);
  }
);

Retries and delivery

Respond with any 2xx status to acknowledge a delivery. Anything else (or a timeout - we wait up to 15 seconds per attempt) is treated as a failure and retried up to 5 times with exponential backoff:

AttemptWhen
1st retry1 minute after the first failure
2nd retry5 minutes later
3rd retry30 minutes later
4th retry2 hours later
5th retry12 hours later, then the delivery is marked failed

You can also open any endpoint in the app to view its recent deliveries (with response status) and manually replay any that failed.

Best practices

  • Verify every signature before acting on a payload, and reject anything that does not match.
  • Respond fast. Acknowledge with 2xx right away and do slow work (emails, syncs) in a background job, so you never hit the 15-second timeout.
  • Be idempotent. A delivery can arrive more than once on retries. Use X-Zoye-Delivery (or the contact id plus event) to skip duplicates.
  • Tolerate new fields. We may add fields to data over time. Ignore unknown fields rather than failing.

Want the other direction? See No-code embed, REST API, and MCP server for getting contacts into Zoye.

PreviousMCP serverNextRate limits
Zoye LogoZoye Logo

עוזר AI שמנהל את סביבת העבודה העסקית שלכם

hello@zoye.io
מוצר
  • עוזר AI
  • תמחור
  • בלוג
  • מדריך סנכרון משתמשים
חברה
  • אודות
  • Discord
  • נסו בחינם
זמין ב
  • English
  • עברית
  • Français
  • Español
  • Русский
  • Magyar
  • हिन्दी
  • Polski
  • Deutsch
  • Português
  • Nederlands
  • Italiano

© 2026 Zoye AI. כל הזכויות שמורות. נבנה לעתיד העבודה.

מדיניות פרטיותתנאי שימוש