API & webhooks

Get events with webhooks

Have Eden post sales, signups, bookings, scheduled posts, and more to your own app or automation tool the moment they happen.

·4 min read

Go to Settings → API, click Add endpoint under Webhooks, paste an https address, and pick your events. Eden posts each event there as it happens, signed with the webhook's secret.

Add a webhook

  1. 01

    Open Settings → API

    Webhooks belong to the workspace you have open, and get that workspace's store and post events. Have more than one workspace? Open the right one first.

  2. 02

    Click Add endpoint

    Paste the address that should get events. It must start with https:// and be on the public internet.

  3. 03

    Pick events

    Keep All events, including new ones, or untick it and pick only the ones you need.

  4. 04

    Copy the signing secret

    Eden shows it once. You only need it if your own code checks signatures.

Click a webhook to Send test event, Roll secret, Turn off, or Delete it. Recent deliveries shows the last 30 days, with Resend on each one.

Webhooks need Starter or above, and up to 20 fit in each workspace. You don't need a store: post events work in any workspace, and store events start once you set up a store.

Events

GroupEvents
Salespurchase.created, purchase.refunded, purchase.membership_ended, membership.renewed, membership.cancelled
Customerscustomer.created, affiliate_sale.created
Emaillead.created, contact.tagged, form.completed
Callsbooking.created, booking.cancelled, booking.rescheduled, coaching_application.created
Courses and reviewslesson.completed, lesson_question.created, cohort_signup.created, review.created, testimonial.created
Ask mepaid_question.created
Postspost.published, post.failed

purchase.created covers Eden checkout, free claims, and sales your own checkout reports to Eden.

Post events

post.published fires when a scheduled post goes out on at least one platform. post.failed fires when it fails on every platform. Eden sends either one once every platform is done, so a post that waits on Substack sends when Substack finishes.

data has post_id, status (posted, partial, or failed), text, scheduled_for, posted_at, url (the first live link), links, error, and platforms: one entry per platform with its status, url, and error. They go to the webhooks of the post's workspace.

What Eden sends

A JSON POST like this:

{
  "id": "evt_3f2a9c0d8b7e4a51a2c3d4e5f6a7b8c9",
  "type": "booking.created",
  "created_at": "2026-10-04T17:00:00.000Z",
  "store": { "handle": "yourname" },
  "data": { "id": "booking:abc", "email": "[email protected]" }
}

data has the same fields as the matching list in the API reference. The five sale, signup, and form events keep the fields the store webhook always sent.

Each event comes once per webhook. If one could arrive twice (a resend, a retry after your app timed out), use id to skip the repeat.

Check the signature

Every post has these headers:

  • X-Eden-Signature: sha256= and an HMAC-SHA256 of <timestamp>.<body> made with your secret.
  • X-Eden-Timestamp: when Eden signed it, in milliseconds.
  • X-Eden-Event and X-Eden-Event-Id: the type and the id.
import { createHmac, timingSafeEqual } from "node:crypto";

function fromEden(rawBody, headers, secret) {
  const expected = createHmac("sha256", secret)
    .update(`${headers["x-eden-timestamp"]}.${rawBody}`)
    .digest("hex");
  const given = String(headers["x-eden-signature"]).replace("sha256=", "");
  return (
    given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected))
  );
}

The same check in Python:

import hashlib, hmac

def from_eden(raw_body: bytes, headers, secret: str) -> bool:
    signed = headers["X-Eden-Timestamp"].encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    given = headers.get("X-Eden-Signature", "").removeprefix("sha256=")
    return hmac.compare_digest(given, expected)

Check the raw body, before your framework parses it.

Retries

  • Answer with any 2xx within 10 seconds and the event is done.
  • Anything else, Eden tries again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours, and 24 hours.
  • Eden doesn't follow redirects. Use the final address.
  • After 3 days with nothing but failures, Eden turns the webhook off and emails you. Fix it, then click Turn on.

Missed some while your app was down? GET /store-app/v1/events lists every event from the last 30 days.

Your store webhook from before

The webhook on your store's Integrations page now shows here too, marked Your store webhook. It keeps its address, secret, five events, and body, so your Zaps keep working. It now gets retries and a delivery log, and sales from your own checkout reach it too.

If something's not working

What you seeWhat to do
"That address points at a private network"Use a public https:// address. Eden can't post to local or internal addresses.
Gave up on a deliveryYour app didn't answer 2xx after every retry. Fix it, then click Resend.
Signatures never matchCheck the raw body, not parsed JSON, and make sure you copied the newest secret.
Still stuck?

Email us. A real person reads every message.

Tell us what you tried and where you got stuck. We answer within one business day.

[email protected]