Custom Integrations — Extensions
Extensions let your own logic run inside Galactic Core’s own flows — reacting to store events, or standing in for a built-in integration at the seam where the platform calls one. Registration, signing, retries, revalidation and run logging are handled by the platform. (The other route to a Custom Integration runs your own service entirely outside the platform and drives it over the API: Bring Your Own Compute, for orchestration an Extension doesn’t cover.) If what you need is somewhere to record information the platform does not model, rather than logic to run, that is Custom Fields. With Extensions there are two things you can build:Automations
React to a store event —
order.paid, product.stock_low, cart.abandoned — with a no-code recipe or a small custom function. “When X happens, call my system / sync a field / send a message.”Custom Providers
Become the implementation Galactic Core calls for a capability — Email, SMS, Marketing, Tax, Shipping rates, Sales channels, Payments, or shopper sign-in — standing in for the built-in integration.
Where extensions live: you create and manage extensions in your Tybrite dashboard under Integrations → Extensions — not through the storefront API. A recipe builder, a code editor, a remote-endpoint registration form, and a live run-log debugger are all there.
The two building blocks
Automations
An automation runs after the fact, off a real store event, and never blocks anything. You author it two ways:- A recipe — a small, validated JSON graph (
trigger → filter → transform → action) with no raw code. Ideal for the “notify my system / call an API onorder.paid” case. - A custom function — a small handler when you need real logic.
Custom providers
A custom provider — one you build and connect yourself — implements one of these eight named interfaces, and when selected is dispatched at that subsystem’s seam in place of the built-in path. (Advertising and Accounting sync are also provider-backed, but Galactic Core builds and runs those for you — see the note below the table — so they are not in this build-your-own set.)For single-active interfaces (Email, SMS, Tax, Marketing, Auth) selecting a custom provider replaces the built-in for that capability. Shipping adds a rate source next to the native ones; Sales channels are fully multi-active; Payments route per transaction, so your custom method runs alongside the native processors.
AuthProvider is remote-only and fail-closed. It verifies who a shopper is, so it must run as a remote endpoint you host, must be synchronous, and if it rejects a token or can’t be reached the request is treated as signed-out — never signed-in. It is the second of the two bring-your-own-auth modes: with the first, your backend verifies the shopper and signs an assertion Galactic Core checks (x-external-auth); with an AuthProvider, the shopper’s raw identity-provider token is sent to your verifier and Galactic Core trusts the identity it returns (x-idp-token). See Customers and Auth.Advertising and Accounting sync are platform-managed integrations — you connect them rather than build them. Advertising runs your Google/Meta ad campaigns (see Advertising); Accounting sync mirrors every posted ledger entry into QuickBooks so a store’s external books stay current automatically (see Accounting).
Execution modes: hosted vs remote
Every custom function and provider runs in one of two modes. The contract is identical — only where the code runs differs.Hosted
You write a handler; Galactic Core runs it in an isolated sandbox. Your provider secrets are held in our secure vault and injected at run time. Nothing to deploy or operate.
Remote
You run an HTTPS endpoint on your own infrastructure. Galactic Core calls it with a signed request and expects the same return shape. Your provider secrets never leave your side.
Hosted handler
A hosted function is a single default-export handler. Galactic Core passes it the event (or, for sync hooks, the hook input) and a small, safectx:
ctx surface is deliberately tiny — it’s the entire capability set:
There is no database client, no API key, and no access to any other store’s data in
ctx. If your function needs to change store data, it does so the same way any client does — ctx.fetch to the public API with your own key (supplied as a secret alias).
Remote endpoint
In remote mode, Galactic Core POSTs the same input a hosted handler would receive to your registeredendpoint_url, and your endpoint returns the same shape a hosted handler would return. The request is signed so you can verify it came from Galactic Core:
Three details govern whether verification succeeds:
- The signed input is the raw header string and the raw body bytes. Re-serialising the parsed JSON changes key order and spacing, and the signature will not match.
X-Timestampis in milliseconds, so it compares directly againstDate.now().- An absent signature is not an exemption. Galactic Core sends no
X-Signatureat all when an extension has no signing secret configured, so an endpoint that verifies only when the header is present accepts anything from a caller that omits it.
Timestamp / replay: requests carry a 5-minute freshness window — reject anything older. Retries: on a transient failure (a network error or a
5xx), Galactic Core retries the request once, re-signing with a fresh timestamp; a 4xx is treated as final and is not retried. Your endpoint_url must be HTTPS and publicly reachable — internal, link-local, and metadata addresses are rejected.Receiving callbacks from a provider
Some providers do not answer immediately. A mobile-money charge, a bank transfer, or an identity check finishes later and notifies you by posting to a URL. Every extension has one for this:input.callback and operation set to callback. What happens next depends on your
execution mode:
- Remote — the payload is forwarded to your
endpoint_urlas a signed Galactic Core dispatch, with the same headers and verification rules as any other remote call. Your endpoint verifies a request from Galactic Core rather than an unauthenticated one from the provider. - Hosted — your handler is invoked with the payload directly.
Choosing how the callback is authenticated
A public URL that runs your code needs to prove who is calling it, and providers differ in what they can offer. Three modes, configured per extension:
With
hmac, the signature is verified against the raw body before your code runs, and a request arriving
without the header is rejected rather than allowed through — the same rule that governs remote dispatch
in reverse. Bare digests, sha256=…, and t=…,v1=… header formats are all accepted.
Every rejected callback is recorded in the extension’s run log with the reason, so a provider
misconfiguration is visible rather than silent.
Acknowledgement is immediate. The endpoint answers as soon as the request is authenticated and
recorded, before your handler finishes. Providers re-deliver when a response is slow, so waiting on your
code would invite duplicate deliveries. Your handler’s return value is applied through the normal
provider contract, and money-bearing results are revalidated server-side — a callback asserting that a
payment succeeded does not by itself settle an order.Limits: 120 callbacks per minute per extension, and a request body up to 128 KB. A callback counts as
one invocation.
Provider contracts
Each interface operation receives aninput (with an operation discriminator where it has more than one) and must return the shape below. This is the same whether you run hosted or remote. Money-bearing returns (payment status, rate amounts) are revalidated server-side — see Safety.
How this relates to the storefront API. Connecting a custom provider does not change the
storefront API contract your app calls. A shopper’s storefront never sees “which provider handled
this” — the provider is invoked internally behind the endpoints you already use:
- Payments — you still call
POST /v1/payments/initialize/verifyexactly as before. Theproviderfield already accepts any name, and the response shape is unchanged, so a custom payment provider just means a provider name outside the built-in set is handled instead of rejected. (The platform still validates the amount and re-checks the paid status.) - Tax — resolved server-side; it only affects the order’s
tax_amount, which is already part of the order response. - Shipping — the one visible addition:
GET /v1/shipping/calculatemay return extrarates[]from your custom carrier and setrate_sourcetocustom(seecalculateShipping). - Email / SMS / Marketing / Sales channels — entirely off the storefront API (notification and sync internals); there’s nothing to call.
rate_source: custom
value.Billing your own merchants
SubscriptionBillingProvider is the one interface that is not about a shopper. It is how an agency
running its own commerce platform charges its merchants for their plan — the merchant pays the
agency, on the agency’s own payment account, and Galactic Core never touches that money.
Stripe, Lemon Squeezy and Paddle are supported directly, and Paystack in its markets, so an agency
using one of those connects it in their portal rather than building anything. A
SubscriptionBillingProvider covers the remaining cases — a regional gateway outside that set, or an
agency’s own billing system that already holds the customer relationship.
This is a different money flow from
PaymentProvider, which is a shopper paying a merchant at
checkout. Subscription billing is a merchant paying their agency for the platform. They use separate
interfaces so the two can never be resolved through each other’s seam: a shopper’s checkout is never
affected by an agency’s billing choice, and vice versa.Triggers
Automations subscribe to the same event vocabulary as webhooks. The events that fire today:Orders
order.created · order.paid · order.fulfilled · order.shipped · order.cancelled · order.updated · order.refundedPayments
payment.succeeded · payment.failed · payment.refundedCustomers
customer.created · customer.updatedProducts
product.created · product.updated · product.stock_low · product.out_of_stockGift cards
gift_card.issued · gift_card.redeemed · gift_card.expiredCarts & sync
cart.abandoned · feed.sync.completed · channel.sync.completedPromotions
promotion.applied · promotion.created · promotion.activated · promotion.deactivated ·
pricing_rule.created · pricing_rule.activated · pricing_rule.updated · pricing_rule.deactivatedContent & collections
collection.created · collection.updated · post.published · lookbook.published · review.approvedDeletion & restore
product.deleted · promotion.deleted · collection.deleted · post.deleted ·
lookbook.deleted · pricing_rule.deleted · subcategory.deleted · customer.deleted —
the record has left the API. Each has a matching .restored, since deleting is reversible for
90 days; the payload carries restorable_until.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 just as a deleted one would.Store lifecycle
store.updated · payment_provider.connected · shipping_provider.connected · channel.connectedFeature availability
feature.status_changed (a capability crossed between available / awaiting_data / not_in_plan)Safety
Custom code sits on paths a sale runs through, so the platform bounds what a failure in it can reach:Automations never block
Automations run after the event that triggered them. A slow or failing automation can never delay or block checkout, an order, or any request.
Sync providers fail safely
A
ShippingProvider or TaxProvider that errors or times out falls back to the native path — the sale continues. A PaymentProvider failure returns provider unavailable with no order created or changed — never a false “paid”.Opt-in gates
order.validate is fail-open by default (a buggy rule never blocks sales). You can opt a specific gate into fail-closed when you need a hard compliance stop.Galactic Core stays the authority
A custom provider’s claimed payment status and returned rate amounts are revalidated server-side and never trusted as final. The platform remains the price, payment, and ledger authority.
Budgets
Each budget below is a ceiling for its class. Atimeout_ms on your extension can only make its
budget smaller, never larger, so an endpoint written against a longer budget is cut off at the
limit for the class it runs in.
A shopper-facing hook runs while a page render waits on it, which is what sets those budgets in
hundreds of milliseconds. The admin-plane budget is larger because a merchant has clicked a button
and the work happens in front of them: a billing provider makes two round trips, authenticating then
creating, which does not fit inside a checkout budget.
Code review
Stored handler source is scanned on a schedule, and on demand from the Extensions page. The scan reads the code as text — it does not run it — and separates what it finds into two kinds. Corrected automatically. A fault with a single correct answer is repaired in place, because the exposure grows for as long as it stands. A provider key written directly into source is replaced with a placeholder, and a request addressed to an unencryptedhttp:// endpoint is moved to https://. Each
change keeps a before-and-after you can read and undo, and the stored before-image has the credential
stripped out of it, so the record of the fix does not preserve the thing it removed. A redacted key
means the handler will now fail to authenticate rather than continue running on an exposed
credential — connect it under Integrations and read it by name.
Reported for you to decide. Anything where a reasonable author could disagree is left alone and
raised as a suggestion, since a rewrite changes what the extension does. The most common one is reading
a connected credential as ctx.secrets['name'] rather than await ctx.secrets.get('name'): the first
form returns nothing, so the request goes out unauthenticated and the provider’s refusal looks like an
account problem rather than a bug.
Scanning and automatic correction are separate settings. Turning correction off keeps the scan and
reports everything instead, including the faults that would otherwise be repaired. Where a store is
managed by an agency, the agency decides whether its merchants are scanned.
The scan reasons about text, so it recognises the patterns above rather than understanding your
logic. It is a floor under the obvious mistakes, not a substitute for reviewing what an extension
does with the data it touches.

