Guides

Wire a governed connection

Stripe, Twilio, SendGrid, your internal gRPC service — every external integration is a typed, governed connection.

Authored code never opens raw sockets, never stores plaintext secrets, never makes ungoverned outbound calls. Every external integration is modeled as a governed connection — a typed platform entity with secret references, capability descriptors, egress policy, and observability hooks.

Connection types

Vadyl ships first-class connection types for common integrations:

  • http — generic HTTP / OAuth2 / OAuth1
  • llm — model inference adapters
  • email — SendGrid, Resend, SMTP
  • sms — Twilio, MessageBird
  • payment — Stripe, Adyen, Paddle
  • broker — Kafka, NATS, RabbitMQ
  • search — Elasticsearch, Algolia, Typesense
  • kms — AWS KMS, Azure Key Vault, GCP KMS
  • grpc — internal gRPC services
  • custom — for anything not in the catalog (declarative bundle or Wasm-authored)

Define a connection

// src/connections/stripe.ts
import { connection, secret } from "@vadyl/sdk";


export default connection.define("stripe", {
  type: "payment",
  vendor: "stripe",
  baseUrl: "https://api.stripe.com",
  auth: {
    kind: "bearer",
    token: secret.ref("STRIPE_SECRET_KEY"),  // never inline a string
  },
  egress: {
    allowedHosts: ["api.stripe.com"],
    timeoutMs: 30_000,
  },
  capabilities: {
    operations: ["createCharge", "createCustomer", "refund", "listCharges"],
    requiresIdempotencyKey: true,
  },
  observability: {
    redactRequestFields: ["card.number", "card.cvc"],
    redactResponseFields: ["payment_method_details"],
  },
});

Use it from authored code

// src/handlers/orders/charge.ts
export default handler.core(async (ctx, input: { orderId: string }) => {
  const order = await ctx.entities.Order.read(input.orderId);

  // The SDK is typed against the connection's declared operations
  const charge = await ctx.connections.stripe.createCharge({
    amount: order.total,
    currency: order.currency,
    customer: order.customerId,
    idempotencyKey: `charge:${order.id}`,
  });

  await ctx.entities.Order.update(order.id, {
    status: "paid",
    chargeId: charge.id,
  });

  return { ok: true, chargeId: charge.id };
});

Secret references

Secrets are resolved through the project's ISecretProvider binding — environment variables in dev, AWS Secrets Manager / Azure Key Vault / GCP Secret Manager / a custom KMS in production. The plaintext value never appears on connection records, never appears in observability data, never appears in branching diffs.

Egress policy

Every governed connection declares an allowlist of egress hosts. Calls to non-allowlisted hosts fail closed with a typed error. This is enforced at the runtime bridge level — not by you remembering to add a check.

Per-environment overrides

Use a different Stripe account in staging vs production:

// vadyl.config.ts
environments: {
  staging:    { secrets: { STRIPE_SECRET_KEY: "stripe-test-key" } },
  production: { secrets: { STRIPE_SECRET_KEY: "stripe-live-key" } },
},

Observability

Every connection invocation produces an operational trail with request/response sizes, latency, status, and redacted payload. Failures classify by typed error kind (auth, rate-limited, timeout, provider-error, network) — never by string-matching exception messages.

Managed HTTP escape hatch

For ad-hoc integrations not yet first-class, use ctx.http — a managed generic HTTP client that still flows through allowed-host enforcement, secret references, and audit. Raw fetch() from authored code is rejected at compile time.