Integration Binding

Integration binding connects your app's capabilities to external service providers. Each tenant can configure their own integration connections while sharing the same app blueprint.

How it works

Integration binding follows a three-layer model:

  1. IntegrationSpec (global) - Defines what an integration provides
  2. IntegrationConnection (per-tenant) - A tenant's configured connection
  3. AppIntegrationBinding (per-app) - Maps named slots to concrete tenant connections

Example: Payment processing

Let's walk through a complete example of binding Stripe for payments.

Step 1: Blueprint declares requirement

// AppBlueprintSpec
{
  meta: { name: "invoice-app", version: 1, appId: "invoice" },
  integrationSlots: [
    {
      slotId: "payments.primary",
      requiredCategory: "payments",
      allowedModes: ["managed", "byok"],
      requiredCapabilities: [
        { key: "payments.createPaymentIntent", version: 1 },
        { key: "payments.createRefund", version: 1 }
      ],
      required: true,
      description: "Primary PSP used for Checkout and Subscription flows"
    }
  ],
  workflows: {
    checkout: { name: "invoice.checkout", version: 1 }
  },
  branding: {
    appNameKey: "invoice.appName",
    assets: [{ type: "logo", url: "https://cdn.acme.dev/logo.png" }],
    colorTokens: { primary: "colors.brand.primary" }
  }
}

Step 2: Tenant creates connection

// IntegrationConnection
{
  meta: {
    id: "conn_stripe_acme_prod",
    tenantId: "acme-corp",
    integrationKey: "payments.stripe",
    integrationVersion: 1,
    label: "Stripe Production",
    environment: "production",
    createdAt: "2025-01-15T09:00:00Z",
    updatedAt: "2025-01-15T10:30:00Z"
  },
  ownershipMode: "byok",
  externalAccountId: "acct_123",
  config: {
    accountId: "acct_123",
    webhookUrl: "https://acme.app/webhooks/stripe"
  },
  secretRef: "vault://integrations/acme/conn_stripe_acme_prod",
  status: "active"
}

Step 3: TenantAppConfig binds connection

// TenantAppConfig
{
  meta: {
    tenantId: "acme-corp",
    blueprintName: "invoice-app",
    blueprintVersion: 1,
    environment: "production",
    version: 4,
    status: "published"
  },
  integrations: [
    {
      slotId: "payments.primary",
      connectionId: "conn_stripe_acme_prod",
      scope: {
        workflows: ["checkout", "subscription-renewal", "refund-process"],
        operations: ["payments.stripe.*"]
      },
      priority: 1
    }
  ],
  branding: {
    appName: { en: "Acme Billing Portal" },
    customDomain: "billing.acme.com",
    assets: [{ type: "logo", url: "https://assets.acme.com/logo.svg" }]
  }
}

Step 4: Runtime resolves and executes

// ResolvedAppConfig (runtime)
{
  integrations: {
    "payments.createPaymentIntent": {
      provider: { /* IntegrationSpec for Stripe */ },
      connection: { /* IntegrationConnection */ },
      config: { /* Merged configuration */ }
    }
  }
}

// When workflow executes:
const result = await executeCapability(
  "payments.createPaymentIntent",
  { amount: 5000, currency: "usd" },
  resolvedConfig
);
// → Uses Stripe connection automatically

Multi-integration scenarios

A single app can use multiple integrations across different categories:

integrations: [
  {
    slotId: "payments.primary",
    connectionId: "conn_stripe_prod",
    scope: {
      workflows: ["checkout", "subscription-*"]
    }
  },
  {
    slotId: "email.outbound",
    connectionId: "conn_postmark_prod"
  },
  {
    slotId: "email.inbound",
    connectionId: "conn_gmail_support",
    scope: {
      workflows: ["support-ticket-creation"]
    }
  },
  {
    slotId: "knowledge.vector-store",
    connectionId: "conn_qdrant_prod",
    scope: {
      workflows: ["semantic-search", "rag-query"]
    }
  }
]

Sandbox vs Production

Tenants typically maintain separate connections for sandbox and production environments:

// Sandbox environment
{
  meta: { environment: "sandbox", status: "preview" },
  integrations: [
    {
      slotId: "payments.primary",
      connectionId: "conn_stripe_acme_test"
    }
  ]
}

// Production environment
{
  meta: { environment: "production", status: "published" },
  integrations: [
    {
      slotId: "payments.primary",
      connectionId: "conn_stripe_acme_prod"
    }
  ]
}

Security & validation

  • Integration connections are validated before binding - health checks ensure connectivity
  • Secrets are never stored in TenantAppConfig - only references to encrypted secrets
  • Policy Decision Point (PDP) enforces which workflows can use which integrations
  • All integration calls are audited with full request/response logging
  • Rate limiting and quotas are enforced per connection

Best practices

  • Use wildcard patterns sparingly in allowedWorkflows - be explicit about access
  • Always maintain separate sandbox and production connections
  • Monitor integration health checks and set up alerts for failures
  • Document the purpose of each integration binding for your team
  • Test integration changes in sandbox before promoting to production