Integration Platform
Learn where a ticket adapter fits, which responsibilities stay in Diem, and how trusted provider code is discovered and configured.
On this page
A reviewed, deployment-time extension point
A ticket service joins Diem through a small adapter. The adapter speaks the provider's API and maps upstream responses into canonical account, group, event, ticket, and fulfillment data. The integration platform validates that contract and owns authentication orchestration, persistence, synchronization, public responses, and durable fulfillment state.
Adapters are trusted application modules. An internal adapter is packaged with the API deployment; an external adapter directory or package must also be explicitly included in that artifact. Discovery never turns an arbitrary client-supplied module name into executable code.
Keep the adapter transport-focused
| Concern | Adapter | Integration platform |
|---|---|---|
| Provider protocol | HTTP calls, pagination, provider rate limits, bounded retries, and response mapping. | Supplies an abort signal and applies bounded persistence concurrency. |
| Authentication | Implements the declared login, challenge, OAuth, token, refresh, and optional revocation hooks. | Validates fields, signs challenge state, resolves ownership, stores credentials, and rate-limits attempts. |
| Database state | Reads the resolved Provider document but never writes Mongo directly. | Owns Provider, Provider_Account, group, resource, ticket, and fulfillment persistence. |
| Synchronization | Returns canonical items, safe per-item errors, and truthful counts. | Runs connect-time, manual, and scheduled sync and reconciles complete snapshots. |
| Fulfillment | Performs one logical upstream issuance and returns a safe provider reference. | Normalizes items, enforces idempotency, records leases/results, retries safe failures, and gates confirmation. |
- Do not call Diem HTTP routes from an adapter; invoke the upstream provider only.
- Do not create local tickets, purchases, or fulfillment rows from provider code.
- Do not accept Diem owner, user, Provider, account, or group IDs from upstream payloads.
- Do forward the platform abort signal through every request and pagination loop.
Provider identity, configuration, and pricing live in Mongo
Code has two stable identifiers: the lowercase kebab-case manifest.keyand the stable manifest.provider.name. Mongo creates the environment's real Provider._id. At catalog initialization, Diem resolves the row by key, falls back to the canonical name or declared legacy alias, creates a row only when needed, and binds that database ID to the registry at runtime.
| Owner | Examples | Rule |
|---|---|---|
| Manifest | Key, semantic version, display name, description, aliases, capabilities, auth presentation, action flags. | Stable code contract only; never include an ObjectId, provider application secret, or default user token. |
| Provider row | _id, enabled state, public app presentation, Provider.config, application credentials, checkout-fee policy. | Environment-owned operational state. Preserve protected config and operator-reviewed app copy when catalog metadata is refreshed. |
| Provider account | Connected user's access token, refresh token, upstream account identity, reconnect state, integration_fee_payer setting. | User-scoped credential and preference material; separate from provider-level application credentials. |
Ticket integration fees are policy, not adapter behavior. A provider that imports ticket inventory requires a validated Provider.config.checkout_feepolicy. The adapter returns the upstream base ticket price; the platform snapshots the policy on the imported event and calculates the fee at checkout from the selected base ticket lines.
{
"checkout_fee": {
"version": 1,
"currency": "USD",
"components": [
{
"key": "provider_service_percentage",
"label": "Provider service fee",
"type": "percentage",
"scope": "ticket",
"basis": "base",
"rate_bps": 500
},
{
"key": "provider_order_percentage",
"label": "Provider order fee",
"type": "percentage",
"scope": "purchase",
"basis": "subtotal_with_prior_fees",
"rate_bps": 250
}
],
"default_payer": "purchaser",
"allowed_payers": ["purchaser", "merchant"],
"source_url": "https://provider.example/pricing",
"verified_at": "2026-08-01T00:00:00.000Z",
"summary": "5% per paid ticket, plus 2.5% per order"
}
}A component has a snake-case key, public label,fixed or percentage type, ticket orpurchase scope, and a base orsubtotal_with_prior_fees basis. Percentage rates use integer basis points in rate_bps; fixed amounts and optional inclusivewhen.min_amount_minor/max_amount_minor bounds use integer minor units. Ticket-scoped components precede purchase-scoped components and all components run in declaration order. Free tickets remain fee-free.
default_payer must be present in allowed_payers. Each connected account stores its current choice inProviderAccount.settings.integration_fee_payer. The authenticated seller app updates it with PATCH /integrations/accounts/:accountId/settings and body { "integration_fee_payer": "purchaser" | "merchant" }. The platform validates the requested payer against the Provider policy; adapters do not read or calculate it.
Checkout writes an immutable Purchase.checkout_pricing snapshot withversion, provider_key, currency,fee_payer, base_subtotal_minor,integration_fee_minor, purchaser_charge_minor,merchant_net_minor, the policy, and calculated components. Replay, receipts, reporting, and payer-aware full refunds use that snapshot instead of the current Provider row.
Discovery must match the deployment artifact
Built-in Bookt, Eventbrite, and Posh adapters use literal loaders incomponents/integrations/providers/index.js. The static imports are intentional: esbuild must see each module to include it in a SAM/Lambda artifact. The scaffold updates the adapter and sorted loader together.
- The registry can scan
components/integrations/providers/*-adapter.jsin an unbundled or explicitly mounted adapter directory. - A trusted installed package can be listed in the deployment-owned
DIEM_INTEGRATION_MODULESsetting. - An external directory or package must already be copied, mounted, or installed in the deployed artifact; filesystem discovery cannot make a bundler include unseen code.
- Aliases and database Provider IDs resolve only after the registry has validated and bound the canonical integration.
Eventbrite deliberately combines official and organizer APIs
Eventbrite profile, organization, and event discovery use the official API. Organizer login, private ticket-class inventory, and guest-list ticket issuance use separate organizer-authenticated/private flows. The adapter keeps that split behind one canonical contract, but the two upstream surfaces are not interchangeable.
Resolve the organizer application token
Read
Provider.config.organizer_app_token. The legacylogin_tokenkey exists only during migration; there is no environment fallback.Authenticate the organizer
Validate the provider user's credentials and store only the returned connected-user token on that Provider_Account.
Validate official reads
Fetch the profile, organizations, and events through the official API and verify pagination and ownership.
Validate private operations separately
Use the connected-user token for private inventory and guest-list issuance only after proving that the target environment accepts it for each operation.