SDK application development
The Qefro SDK connects external systems to Qefro over /qefro.
It is not how you build a Marketplace App.
Marketplace Apps are metadata executed by Qefro Runtime — start at Build your first app and Runtime vs SDK.
Use this page for Focus ERP, Yaaz, ABM, on-prem POS, or any customer system of record.
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 whether the adapter is an
org SDK Connection or a packaged hosting: external process. Marketplace
Apps are metadata and do not use this SDK.