Headlessly

Integrations

Connect Stripe, GitHub, webhooks, and external services as typed entities with event forwarding.

Integrations are not plugins. They are entities with verbs, events, and lifecycle management. Connect an external service, and headless.ly maps its data to your entity graph bidirectionally.

import { Integration } from '@headlessly/platform'

await Integration.connect({ provider: 'stripe', apiKey: process.env.STRIPE_KEY })
await Integration.connect({ provider: 'github', token: process.env.GITHUB_TOKEN })
await Integration.connect({ provider: 'slack', webhook: process.env.SLACK_WEBHOOK })

// All three are now typed entities with status, events, and sync
const active = await Integration.find({ status: 'Active' })

Connect a Provider

The connect verb creates the integration, validates credentials, and starts the initial sync:

import { Integration } from '@headlessly/platform'

const stripe = await Integration.connect({
  provider: 'stripe',
  apiKey: process.env.STRIPE_KEY,
})
// stripe.$id = 'integration_hV6nRxWm'

Once connected, Stripe data flows into Billing entities automatically. Products, Prices, Subscriptions, Invoices, and Payments are bidirectionally synced. Financial metrics (MRR, churn, NRR, LTV) compute from real Stripe data.

Verb Conjugation

Integrations support the full verb lifecycle:

import { Integration } from '@headlessly/platform'

// Execute: connect, disconnect, sync
await Integration.connect({ provider: 'stripe', apiKey: process.env.STRIPE_KEY })
await Integration.disconnect({ id: 'integration_hV6nRxWm' })
await Integration.sync({ id: 'integration_hV6nRxWm' })

// Before hook: runs before the connection is established
Integration.connecting(integration => {
  console.log(`Connecting to ${integration.provider}...`)
})

// After hook: runs after successful connection
Integration.connected(integration => {
  console.log(`${integration.provider} connected -- initial sync starting`)
})

// Sync lifecycle
Integration.syncing(integration => {
  console.log(`Syncing ${integration.provider}...`)
})

Integration.synced((integration, $) => {
  $.Event.create({
    type: 'integration_synced',
    provider: integration.provider,
    entities: integration.lastSyncCount,
  })
})

All Verbs

VerbEventDescription
connectConnectedEstablish the connection and start initial sync
disconnectDisconnectedTear down the connection
syncSyncedForce a manual sync

Stripe

Stripe is the truth source for all Billing entities. See the Stripe reference for entity mapping and webhook events.

import { Integration } from '@headlessly/platform'
import { Subscription } from '@headlessly/billing'

await Integration.connect({ provider: 'stripe', apiKey: process.env.STRIPE_KEY })

// Now Billing entities are Stripe-backed
await Subscription.create({ plan: 'pro', contact: 'contact_fX9bL5nRd' })
// This creates a real Stripe subscription

Subscription.cancelled((sub, $) => {
  $.Contact.update(sub.contact, { stage: 'Churned' })
  $.Campaign.add('campaign_nR4wLxKp', { contact: sub.contact })
})

GitHub

GitHub is the truth source for all Projects entities. See the GitHub reference for entity mapping and webhook events.

import { Integration } from '@headlessly/platform'
import { Issue } from '@headlessly/projects'

await Integration.connect({ provider: 'github', token: process.env.GITHUB_TOKEN })

// Now Projects entities sync with GitHub
await Issue.create({ title: 'Add dark mode', project: 'project_e5JhLzXc' })
// This creates a real GitHub issue

Issue.completed((issue, $) => {
  $.Event.create({ type: 'issue_closed', project: issue.project })
})

Webhooks

Send events to any external URL. Webhooks are integrations with type: 'webhook':

import { Integration } from '@headlessly/platform'

await Integration.connect({
  provider: 'webhook',
  url: 'https://example.com/hooks/headlessly',
  events: ['Deal.Closed', 'Subscription.Created', 'Ticket.Escalated'],
  secret: process.env.WEBHOOK_SECRET,
})

Every matched event sends a POST with the event payload and an HMAC signature header for verification:

{
  "event": "Deal.Closed",
  "timestamp": "2025-09-15T14:32:00Z",
  "data": {
    "$id": "deal_k7TmPvQx",
    "name": "Enterprise",
    "value": 50000,
    "contact": "contact_fX9bL5nRd"
  }
}

Event Forwarding

The browser SDK captures client events and forwards them to external analytics providers while storing a copy in your Iceberg R2 lakehouse:

<script src="https://js.headless.ly/v1" data-tenant="my-startup" />

Configure forwarding destinations as integrations:

import { Integration } from '@headlessly/platform'

await Integration.connect({
  provider: 'analytics',
  destinations: {
    ga: { measurementId: process.env.GA_ID },
    sentry: { dsn: process.env.SENTRY_DSN },
    posthog: { apiKey: process.env.POSTHOG_KEY },
  },
})

Events flow to all configured destinations simultaneously. The lakehouse copy grows over time, enabling you to phase out external tools as headless.ly's analytics capabilities expand.

Sync Status

Check the health of all integrations:

import { Integration } from '@headlessly/platform'

const integrations = await Integration.find({ status: 'Active' })
for (const i of integrations) {
  console.log(`${i.provider}: ${i.status} -- last sync ${i.lastSyncAt}`)
}

// React to errors
Integration.errored((integration, $) => {
  $.Agent.notify('agent_pR7xMwKn', {
    message: `Integration ${integration.provider} failed: ${integration.error}`,
  })
})

MCP

Connect an integration via MCP:

headless.ly/mcp#do
await $.Integration.connect({
  provider: 'stripe',
  apiKey: process.env.STRIPE_KEY,
})

List active integrations:

headless.ly/mcp#search
{ "type": "Integration", "filter": { "status": "Active" } }

Force a sync:

headless.ly/mcp#do
await $.Integration.sync({ id: 'integration_hV6nRxWm' })

CLI

npx @headlessly/cli Integration.connect --provider stripe --apiKey $STRIPE_KEY
npx @headlessly/cli Integration.connect --provider github --token $GITHUB_TOKEN
npx @headlessly/cli Integration.sync integration_hV6nRxWm
npx @headlessly/cli Integration.disconnect integration_hV6nRxWm

On this page