PlatformXeDocs
Get API Key

Custom Events Marketplace

Publish your registered events to a shared marketplace so other tenants can fork the schema into their own org. Pro+.

The client.events.custom.marketplace namespace lets PRO+ tenants share event schemas across organisations. A publisher exposes a registered event as a listing; another PRO+ tenant forks the listing — the platform copies the schema into the fork's org as a fresh registration the fork tenant fully owns. Forks are independent thereafter (no upstream propagation). Available in TypeScript, Python, Go, and Terraform.

SDKInstallNamespace
TypeScriptnpm install @caldera/platformxe-sdk@^1.3.0client.events.custom.marketplace
Pythonpip install platformxe>=1.2.0client.events.custom.marketplace
Gogo get github.com/calderax/platformxe-go@v1.2.0client.Events.Custom.Marketplace
Terraformsource = "calderax/platformxe" ≥ 1.2.0platformxe_marketplace_listing

Plan tiers

CapabilityFreeBasicProEnterprise
Browse listings
Publish your own
Fork a listing
Unpublish / republishn/an/a✅ (owner)✅ (owner)

What forking does

When you fork a listing:

  1. The platform creates a new customEventRegistrations row in your org with the listing's schema copied verbatim.
  2. The fork shows up as a normal registered event — you can emit against it, archive it, bump versions, all independently.
  3. A marketplace_forks row records source attribution (publisher org id + source canonical name) so audit logs preserve provenance.
  4. The publisher's fork_count increments — that's the only thing the publisher sees.

Forks do NOT auto-update. When a publisher pushes a new version of an event they previously listed, your fork is unaffected. To pick up the new shape, fork the new listing.

Quick start — browse + fork

// Browse
const { listings } = await client.events.custom.marketplace.list({
  search: 'property',
  limit: 10,
});

// Look at one
const detail = await client.events.custom.marketplace.get(listings[0].id);
console.log(detail.payloadSchema);

// Fork it into your org under your own naming
const fork = await client.events.custom.marketplace.fork(listings[0].id, {
  namespace: 'lettings',           // must be on your org's allowlist
  name: 'property.favorited',      // your local name
  // version defaults to '1.0.0' — initial version of the forked event
  // status  defaults to 'draft' — fork doesn't auto-publish
});
console.log(fork.forkCanonicalName);  // 'TENANT_CUSTOM:org_yours:lettings.property.favorited@1.0.0'
console.log(fork.sourceCanonicalName); // attribution to the publisher
listings = client.events.custom.marketplace.list(search="property", limit=10)
detail = client.events.custom.marketplace.get(listings["data"]["listings"][0]["id"])
fork = client.events.custom.marketplace.fork(
    listings["data"]["listings"][0]["id"],
    namespace="lettings",
    name="property.favorited",
)
out, _ := client.Events.Custom.Marketplace.List(platformxe.ListMarketplaceParams{
    Search: ptr("property"),
    Limit:  ptr(10),
})
detail, _ := client.Events.Custom.Marketplace.Get(out.Listings[0].ID)
fork, _ := client.Events.Custom.Marketplace.Fork(out.Listings[0].ID, platformxe.ForkMarketplaceInput{
    Namespace: "lettings",
    Name:      "property.favorited",
})

Publish

// You've already registered this event (Phase 9A) and bumped it to status='published'.
const listing = await client.events.custom.marketplace.publish({
  registrationId: 'cer_…',
  title: 'Property favorited',
  description: 'Fired when a customer favorites a property in a lettings agent UI.',
  tags: ['lettings', 'crm', 'favorites'],
});
listing.id;                    // 'mkl_…'
listing.sourceCanonicalName;   // 'TENANT_CUSTOM:your-org:lettings.property.favorited@1.0.0'
listing.status;                // 'published'
resource "platformxe_marketplace_listing" "property_favorited" {
  registration_id = platformxe_custom_event.property_favorited.id
  title           = "Property favorited"
  description     = "Fired when a customer favorites a property in a lettings agent UI."
  tags            = ["lettings", "crm", "favorites"]
}

Validation:

  • Source registration must be owned by the publishing org and status = 'published'.
  • The platform-seeded generic event (platformxe.generic) cannot be listed.
  • Listings are pinned per (orgId, namespace, name, version). Re-publishing the same triple returns 409 listing_already_exists — bump the source event's version to publish a new shape.

Unpublish + republish

// Hide the listing — existing forks are unaffected.
await client.events.custom.marketplace.unpublish('mkl_…');

// Re-activate later if you change your mind.
await client.events.custom.marketplace.republish('mkl_…');

Archived listings (status = 'archived') cannot be republished — create a new listing for the next version instead.

Browse

const result = await client.events.custom.marketplace.list({
  namespace: 'lettings',          // optional namespace filter
  status: 'published',            // default; pass 'unpublished' to widen
  search: 'property',             // free-text on title + description
  limit: 25,                      // [1..100]
  offset: 0,
});
result.listings;  // MarketplaceListingSummary[]
result.total;     // total count (un-paginated)

Sort order is published_at DESC, id ASC.

Reference — wire shapes

PublishMarketplaceRequest:

{
  registrationId: string;     // source registration to publish
  title: string;              // min 3 chars
  description?: string;
  tags?: string[];
}

MarketplaceListingSummary (returned from list/publish/republish):

{
  id: string;                              // 'mkl_…'
  publisherOrganizationId: string;
  namespace: string;
  name: string;
  version: string;
  sourceCanonicalName: string;             // TENANT_CUSTOM:pub-org:ns.name@v
  title: string;
  description: string | null;
  tags: string[];
  status: 'published' | 'unpublished' | 'archived';
  forkCount: number;
  publishedBy: string;
  publishedAt: string;
  unpublishedAt: string | null;
  createdAt: string;
  updatedAt: string;
}

MarketplaceListingDetail extends summary with payloadSchema: object and payloadExample: object | null.

ForkMarketplaceRequest:

{
  namespace: string;       // must be on fork org's allowlist + match /^[a-z][a-z0-9_-]{1,30}$/
  name: string;            // matches /^[a-z][a-z0-9_.-]{1,60}$/
  version?: string;        // defaults to "1.0.0" — initial version of the forked event
  description?: string;    // optional override; falls back to listing description
  status?: 'draft' | 'published';  // default 'draft'
}

ForkMarketplaceResponse:

{
  forkId: string;                   // 'mkf_…' (the marketplace_forks row)
  forkRegistrationId: string;       // 'cer_…' (the customEventRegistrations row in your org)
  forkCanonicalName: string;        // TENANT_CUSTOM:your-org:ns.name@v
  sourceCanonicalName: string;      // attribution
}

Reference — error codes

CodeStatusCause
MARKETPLACE_PUBLISH_NOT_AVAILABLE402Free / Basic plan
MARKETPLACE_FORK_NOT_AVAILABLE402Free / Basic plan
invalid_title400< 3 chars
registration_not_found404Source registration not owned by publisher org
registration_not_published409Source must be status='published'
registration_is_generic400The platform-seeded platformxe.generic event cannot be listed
listing_already_exists409Same (orgId, namespace, name, version) already listed — bump source version
listing_not_found404Unpublish / republish / fork against unknown listing (or non-owner for mutations)
already_unpublished409Unpublishing a listing already in unpublished/archived
already_published409Republishing a listing already published
archived_listing409Archived listings cannot be republished
listing_not_published409Cannot fork a listing that is not currently published
invalid_namespace / invalid_name400Fork target name fails the regex
namespace_not_allowed400Fork namespace not on the fork org's allowlist
fork_already_exists409A registration already exists at the fork's (namespace, name, version)

Lifecycle — what fires when

Every state transition emits a typed event on the bus (@caldera/events@^1.24.0). Subscribers (workflow triggers, webhook subscriptions, federation push, audit log) use the bus catalog. The 5 marketplace events:

  • MARKETPLACE_LISTING_PUBLISHED — listing row created
  • MARKETPLACE_LISTING_UNPUBLISHED — publisher hides the listing
  • MARKETPLACE_LISTING_REPUBLISHED — re-activated
  • MARKETPLACE_LISTING_FORKED — a fork row inserted + fork registration created
  • MARKETPLACE_LISTING_FORK_FAILED — fork attempted but blocked by validation/quota/allowlist