SDK application development
Package
npm install @qefro-ai/backend
Import:
import { Qefro } from '@qefro-ai/backend';
// or: import Qefro from '@qefro-ai/backend';
Application shape
Application
├── Tools app.tool
├── Flows app.flow (metadata; runtime executes)
├── Events app.event / webhook / schedule
├── Customer app.customer(provider) optional
├── Marketing app.marketing({…}) metadata
├── Organization app.organization({…}) metadata
└── HTTP app.listen → POST /qefro
Constructor
new Qefro({
signingSecret: string;
protocolVersion?: string; // default '1'
maxTimestampSkewSeconds?: number; // default 300
endpointPath?: string; // default '/qefro'
});
Middleware
app.use(async (ctx, next) => { await next(); });
app.before(async (ctx) => {});
app.after(async (ctx, result) => {});
Tool registration signatures
All supported by app.tool:
app.tool('name', handler);
app.tool('name', metadata, handler);
app.tool('name', handler, metadata);
app.tool({ name: 'name', …metadata }, handler);
See tools.md.
Flows
Flows are metadata only. The SDK advertises them on capabilities.list; the Qefro Runtime orchestrates execution.
app.flow({
id: 'reservation',
version: 1,
trigger: { type: 'conversation' }, // or event / schedule / webhook
})
.ask({ /* … */ })
.tool({ /* … */ })
.complete({ /* … */ });
Events, webhooks, schedules
app.event({ name: 'shopify.order.created', description: '…' }, async (ctx) => {});
app.webhook({ name: 'shipment.delivered' }, async (ctx) => {});
app.schedule({ name: 'daily.digest', cron: '0 9 * * *' }, async (ctx) => {});
Advertised under capabilities.list as events, webhooks, schedules.
Implementation detail: These handlers are stored and advertised only. The SDK /qefro dispatcher does not call them — the Qefro Runtime owns delivery.
Marketing & organization
Call once each:
app.marketing({ version: 1, audiences: […], variables: […], actions: […], landingPages: […], channels: […] });
app.organization({ version: 1, events: […], actions: […], tasks: […] });
Organization capability ids must be opaque (no . / app prefix).
Listen
const handle = await app.listen({ port: 8080, host: '0.0.0.0', path: '/qefro' });
// handle.url → http://0.0.0.0:8080/qefro
// await handle.close();
For embedding in an existing HTTP framework, use handleRaw(body, headers) (verifies signature then dispatches).
Shared code across models
Keep tools, schemas, and domain logic identical for external and managed deployments. Change packaging and registration only — see migration-external-to-managed.md.