Skip to main content
External Event Types are an experimental workspace-level element. The shape of the API and console UI may change before it is generally available.
External Event Types let you subscribe to events from an external app without modifying a Connector — or thinking about connectors at all. Compared to connector-level External Events, an External Event Type is a stand-alone workspace element with its own JavaScript implementation. You define the event in your workspace, point it at an Integration or a specific Connection, and Membrane runs the same subscribe / handle / unsubscribe lifecycle it runs for connector events. Use a stand-alone External Event Type when:
  • The connector you use is shared (for example a public connector from the Membrane Universe) and you don’t want to fork it just to add one event.
  • Different tenants need different events from the same app and managing that variability inside the connector would be awkward.
  • You want to iterate on event handling without going through a connector publish cycle.
External Event Types cannot be used in Flow triggers. The connector-event-trigger node only resolves event keys defined inside a Connector. To consume events emitted by a stand-alone External Event Type, create an External Event Subscription and point it at a webhook URI you control — see Consuming events below.

Scoping

A single External Event Type can live at one of three scopes:
ScopeSet when…Applies to
UniversalNo integrationId or connectionId is setEvery integration in the workspace. Children can be created per integration to override the definition.
IntegrationintegrationId is setAll connections of a single integration.
ConnectionconnectionId is setA single connection (typically used to override the type for one tenant).
This mirrors how stand-alone Actions are scoped — the same “universal → integration → connection” override chain applies.

Definition

External Event Types are managed from the External Event Types page in the console (under the experimental section), or via the /external-event-types API endpoints. Each type has:
FieldDescription
nameHuman-readable name shown in the console.
keyStable identifier used in API calls and exports.
typeImplementation flavour. Currently only webhook is supported, so subscribe, unsubscribe, and handle must all be defined.
parametersSchemaJSON Schema for parameters the subscriber must supply (for example, projectId).
schemaJSON Schema describing the payload of emitted events.
exampleOptional example payload, used for testing and to generate schema.
subscribeRequired. JavaScript function called once when a subscription is created. Registers the webhook with the external system and returns state.
handleRequired. JavaScript function called for each incoming webhook payload. Returns { events, response?, state? }.
unsubscribeRequired. JavaScript function called when the subscription is deleted. Deregisters the webhook.
refreshOptional JavaScript function called periodically to keep the subscription alive when the external app requires it.
The four function slots have the same signatures and semantics as the corresponding connector-level methods documented in Webhook Events — the only difference is where the definition lives.

Example implementation

A minimal end-to-end example for an external app that exposes a /webhooks REST endpoint. The subscribe function registers the webhook and stashes the returned id; handle extracts events from the incoming request; unsubscribe tears the registration down; refresh (optional) extends the registration on a schedule.
subscribe.js
export default async function subscribe({ apiClient, webhookUri, parameters }) {
  const { data } = await apiClient.post('/webhooks', {
    url: webhookUri,
    event: parameters.eventName,
  })

  return {
    state: { webhookId: data.id },
    // Optional — only set if the external app expires webhooks. Omit to disable refresh().
    nextRefreshTimestamp: Date.now() + 24 * 60 * 60 * 1000,
  }
}
handle.js
export default async function handle({ data }) {
  // `data` is the parsed body of the incoming webhook request.
  return {
    events: [
      {
        type: data.action, // e.g. 'created', 'updated', 'deleted'
        record: data.payload,
      },
    ],
  }
}
unsubscribe.js
export default async function unsubscribe({ apiClient, state }) {
  await apiClient.delete(`/webhooks/${state.webhookId}`)
}
refresh.js
export default async function refresh({ apiClient, state }) {
  await apiClient.post(`/webhooks/${state.webhookId}/refresh`)
  return {
    nextRefreshTimestamp: Date.now() + 24 * 60 * 60 * 1000,
  }
}
See Webhook Events for the full argument and return shapes (signature verification, response shaping, state updates from handle, and so on).

Consuming events

Stand-alone External Event Types are not wired to Flow triggers. Instead, you create an External Event Subscription yourself and tell Membrane where to forward incoming events.

1. Create the subscription

POST /external-event-subscriptions with the connection and event type you want to subscribe to:
curl -X POST https://api.getmembrane.com/external-event-subscriptions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "connectionId": "651b…",
    "externalEventTypeId": "67ab…"
  }'
Membrane immediately runs the event type’s subscribe function against the connection. The response includes the new subscription’s id and its initial status (subscribed on success).

2. Attach a webhook URI

Subscriptions created this way do not have a destination yet — incoming events are stored and logged but go nowhere until you set webhookUri:
curl -X PATCH https://api.getmembrane.com/external-event-subscriptions/{id} \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookUri": "https://your-app.example.com/membrane/events"
  }'
From this point on, each event returned by the type’s handle function is POSTed to webhookUri with the event payload as the request body. The URI must be a public http:// or https:// URL — Membrane rejects non-HTTP(S) schemes, loopback, link-local, and other private addresses at delivery time. You can change or clear the destination later by PATCH-ing webhookUri again (use "" to unset). To stop receiving events entirely, DELETE the subscription; Membrane will run the event type’s unsubscribe function before removing the record.

Logs and Troubleshooting

Stand-alone External Event Types use the same monitoring surfaces as connector-level events: