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:
- IntegrationSpec (global) - Defines what an integration provides
- IntegrationConnection (per-tenant) - A tenant's configured connection
- 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 automaticallyMulti-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