order.paid end to end.
All webhook management is secret-key only (sk), so these calls run on your server. A publishable
key returns 403.
1. Registering an endpoint
An endpoint points at your handler URL and lists the events it subscribes to, or["*"] for all of
them. The response includes a signing_secret returned exactly once and never retrievable again — a
lost secret is recovered by deleting the endpoint and re-creating it.
Response
2. Listing and inspecting endpoints and their health
listWebhookEndpoints returns every endpoint with its delivery-health fields: last_success_at,
last_failure_at, consecutive_failures, disabled_reason, and disabled_at. A freshly created
endpoint carries them all at their zero state, null or 0.
Response
getWebhookEndpoint returns the same health fields and adds aggregate delivery_stats for a single
endpoint:
Response
3. Health degradation and auto-disable
Failed deliveries are retried automatically with exponential backoff. Each failure bumpsconsecutive_failures and stamps last_failure_at; a success resets the counter. If an endpoint
fails 20 deliveries in a row, Galactic Core disables it — enabled flips to false, a
disabled_reason is stamped, and disabled_at is set. From then on it receives nothing.
A disabled endpoint reads back like this:
Response
enabled: true re-enables a recovered handler, which also resets the failure counter and
clears disabled_reason:
4. The event envelope
Every delivery is a POST of the same envelope.data.object holds the resource that triggered the
event, and the top-level fields describe it:
type— the event type, such asorder.created.livemode—truefor a live key,falsefor a test key. This is the field that keeps test events away from production systems.api_version— the contract version (v1).environment—productionorsandbox.
order.created envelope as it lands in a handler, taken from the event log:
order.paid envelope for the same order id follows, identical
in shape and differing only in type:
5. Verifying the signature in your handler
Galactic Core signs every delivery and sends the signature in theX-Tybrite-Signature header as
t=<timestamp>,v1=<hex hmac>. A handler recomputes the HMAC over `${t}.${rawBody}` with its
stored signing_secret and compares in constant time before trusting the payload. The example below
is handler code, so it names its own stack:
6. Testing, auditing, and retrying deliveries
A test event exercises connectivity and the signature check end to end. Its payload carriestest: true and placeholder data, so a handler treats it as a no-op rather than fulfilling or
charging against it. The response reports the status_code the endpoint returned.
A handler answering 200 makes the test succeed:
Response
success: false and the
failing status_code. A run of these is what trips the auto-disable in step 3:
Response
type and environment:
Response
retried counts the endpoints hit, and results[] reports each one’s outcome:
Response
7. Handling order.paid end to end
The order is not patched to paid from the webhook. When a payment confirms, Galactic Core has
already marked the order paid, reduced stock, booked the sale, and updated the customer’s metrics.
order.paid fires so that other systems can react to that — pushing the order into an ERP, sending a
custom confirmation email, updating an analytics warehouse, notifying a fulfilment partner.
Supported event types
An endpoint subscribes to any of these, or to["*"] for everything:
- Order lifecycle —
order.created,order.paid,order.fulfilled,order.shipped,order.cancelled,order.refunded,order.updated - Payment —
payment.succeeded,payment.failed,payment.refunded - Customer —
customer.created,customer.updated,customer.deleted - Inventory & catalog —
product.created,product.updated,product.stock_low,product.out_of_stock - Cart & checkout —
cart.created,cart.updated,cart.abandoned - Gift cards —
gift_card.issued,gift_card.redeemed,gift_card.expired - Promotions —
promotion.applied,promotion.created,promotion.activated(a promotion went live or was scheduled to),promotion.deactivated(a live promotion ended, was paused, or expired). A promotion awaiting approval is not yet a promotion: nothing fires while it is a draft, andpromotion.createdarrives when it is approved - Pricing —
pricing_rule.created,pricing_rule.activated(a dynamic-pricing rule went live),pricing_rule.updated(a live rule’s discount, scope, priority, or window changed, so the prices it produces are now different),pricing_rule.deactivated. As with promotions, a rule awaiting approval emits nothing until it goes live - Content & collections —
collection.created,collection.updated(name / banner / homepage placement changed, or membership changed — products added, removed, or re-ordered, carryingmembers_changed: true),post.published,lookbook.published,review.approved - Deletion & restore —
product.deleted,product_variant.deleted,promotion.deleted,collection.deleted,post.deleted,lookbook.deleted,pricing_rule.deleted,subcategory.deleted,customer.deleted. The record has left the API: it is gone from every list, cannot be fetched by id, and stops taking effect — which makes each of these the moment anything built from that record, a page, a menu entry, a mirrored row, no longer has a source. Deleting is reversible for 90 days, so each event has a matching.restoredand the payload carriesrestorable_until, leaving room either to remove a copy now and rebuild it if the restore arrives, or to wait the window out. Deleting a product also deletes its variants, soproduct.deletedis followed by aproduct_variant.deletedfor each - Visibility —
category.activated/category.deactivated,subcategory.activated/subcategory.deactivated. A category switched off is filtered out of every catalog read, so it leaves a storefront exactly as a deleted one would. Categories cannot be deleted — they are a fixed set a merchant enables or disables — so these are the only lifecycle events they emit, and the only signal available to navigation or any layout built from the taxonomy - Store lifecycle & configuration —
store.updated(name / logo / branding / contact / base currency; carrieschanged_fields),payment_provider.connected,shipping_provider.connected,channel.connected - Feature availability —
feature.status_changed(a capability crossed betweenavailable,awaiting_data, andnot_in_plan; carriesfeature,status,previous_status) - Catalog sync & syndication —
feed.sync.completed(a scheduled inbound feed-pull finished — carries created/updated/failed counts),channel.sync.completed(a sales-channel push finished — carries pushed/rejected counts) - Wholesale (B2B) —
b2b.rfq.created,b2b.quote.sent/.accepted/.rejected,b2b.po.issued/.confirmed/.fulfilled,b2b.invoice.issued/.paid/.overdue(for stores with wholesale enabled)
Keeping a storefront in step with the store
The order, payment, and inventory events above track a transaction. The store lifecycle, content and collections, and feature availability events track the store itself changing, and they are what an automation tool listens to to keep a storefront current without anyone rebuilding it. A store rarely arrives fully formed. A merchant starts with a thin catalog and no reviews, no promotions, and no published content, leaving the matching storefront surfaces with nothing to show. As the store fills in, these events mark the moment each surface has something behind it:promotion.activatedcarries the live promotion a banner and product badges render from, andpromotion.deactivatedmarks when they stop applying.review.approvedis the point at which a product page has a real review behind its star summary and reviews block.collection.updatedcovers a banner being added,show_on_homepagebeing turned on, and membership changing, the last carryingmembers_changed: trueand calling for a re-read of the collection’s products. Membership churn is not only a manual edit: a merchant can set a collection to refresh itself on a schedule, so a shelf like “current best sellers” changes contents on its own. The event fires once per collection per change rather than once per product, so a ten-product rotation is one event.pricing_rule.activated/.updated/.deactivatedbound the validity of a cached price. A dynamic-pricing rule changes what every shopper pays without touching a single product record, so a cached price is stale from the moment one of these fires.feature.status_changed(awaiting_data → available) is the moment a capability’s data exists — recommendations shelves once there is enough signal, a currency switcher once a second currency is configured. Anot_in_plan → availabletransition is the same signal arriving after a plan upgrade unlocks the capability.
What’s next
- Pair
feed.sync.completedwith the inbound importer in Catalog Sync to close the loop on scheduled syncs. - See where
order.paidcomes from in the Storefront checkout flow. - See the full envelope shape and per-event payloads in the Webhooks reference.

