Skip to main content

Qefro Python Backend SDK

qefro-backend connects external business systems to Qefro (ERP / POS / CRM) over the signed /qefro webhook.

It is not how you build a Marketplace App. Marketplace Apps are metadata: qefro app init → validate → package → install.

Wire-compatible with the JavaScript and Rust SDKs: same signed protocol, same capabilities.list schema, same 8 flow step types.

Install

pip install qefro-backend

Zero runtime dependencies — it uses only the Python standard library.

Quick start

from qefro_backend import Qefro

app = Qefro(signing_secret="dev-secret")  # or QEFRO_SIGNING_SECRET from env

@app.tool("get_orders", auth="required", lookup={"required": ["email"]})
async def get_orders(ctx):
    customer = ctx.customer.require()
    return [{"orderId": "ord_1", "customerId": customer["id"]}]

app.run(8088)  # POST http://0.0.0.0:8088/qefro

Set the same signing secret in Admin Console → Business Tools → SDK Connections, then Sync Tools. Handlers may be sync or async.

Customer authorization

Register a provider to resolve and verify customers. auth="required" tools authorize automatically before the handler runs; a challenge outcome pauses the tool and asks the customer for a code, then resumes.

from qefro_backend import Qefro, CustomerProvider

app = Qefro("dev-secret")

class Customers(CustomerProvider):
    async def lookup(self, ctx):
        return {"id": ctx.identity.get("phone", "demo")}

    async def authorize(self, ctx):
        if not ctx.response:
            return ctx.auth.sms_otp(ctx.customer["id"], "Enter the code we texted you.")
        if ctx.response.strip() != "123456":
            return ctx.auth.sms_otp(ctx.customer["id"], "Wrong code, try again.")
        return ctx.auth.success(ctx.customer, {"type": "bearer_token", "access_token": "demo"})

app.customer(Customers())

Customer Hub (optional)

When QEFRO_CUSTOMER_HUB_ENABLED=true, tools can call Hub via platform.customer on tool.invoke (or QEFRO_CUSTOMER_HUB_URL + service token). Hub is optional — defaults keep existing apps working (ENABLED=false, OPTIONAL=true). Soft-skip returns None / no-ops when Hub is off or unreachable; set QEFRO_CUSTOMER_HUB_OPTIONAL=false to hard-fail.

@app.tool("create_reservation", auth="none")
async def create_reservation(ctx):
    customer = await ctx.customer.resolve({"whatsapp_number": ctx.identity.get("phone")})
    # Hub properties: ctx.customer.id, .phone_number, .whatsapp_number, .display_name
    await ctx.timeline.append({
        "event_type": "reservation.created",
        "payload": {"code": "R-1001"},
    })
    await ctx.membership.attach({"solution_id": "restaurant-pro"})
    await ctx.consent.grant({"purpose": "marketing"})
    return {"customer_id": customer["id"] if customer else None}

ctx.storage talks to managed storage (ADR-002) the same way as the JS SDK — never call Mongo or storage-service URLs yourself.

Customer Hub Person and Business Events

ctx.person queues Customer Hub mutations (note, tag, assign, activity, merge) for the Qefro runtime. Declare events with app.business_event(...) and publish them with ctx.emit(...) — delivery is outbox → ingest, not CRM automation.

from qefro_backend import Qefro, on_person_created

app = Qefro("dev-secret")
app.business_event({"event_type": "quotation.created", "version": 1})

@app.tool("create_quotation", auth="none")
async def create_quotation(ctx):
    await ctx.person.tag("vip")
    ctx.emit({"event_type": "quotation.created", "event_id": "Q-1001", "data": {"amount": 125000}})
    return {"id": "Q-1001"}

app.flow({"id": "welcome", "trigger": on_person_created()}).message("hi", "Hi").complete("done")

Business Flows

Flows describe how your Business Tools are orchestrated. They are metadata only — the SDK advertises them through capabilities.list and the Qefro Runtime discovers, validates, versions, and executes them. Nothing runs inside the SDK.

(
    app.flow({
        "id": "order_lookup",              # immutable identity — renaming `name` never creates a new flow
        "name": "Order Lookup",
        "description": "Look up customer orders",
        "category": "crm",
        "tags": ["customer", "orders"],
        "intent": ["track order", "where is my order"],
        "inputs": ["email"],
        "outputs": ["get_orders"],
    })
    .ask("email", field="email", prompt="Please enter your email.")
    .tool("orders", tool_ref="get_orders")
    .complete("done", message="Here are your recent orders.")
)

Every step needs a unique id; tool steps reference an existing Business Tool by tool_ref. Step builders: .ask() .tool() .challenge() .upload() .condition() .delay() .approval() .complete() .message() .tag() .assign() .activity(). A duplicate/empty flow or step id raises FlowError. See examples/order-approval for a full ask → tool → condition → approval → OTP-authenticated tool → complete flow.

Protocol

Message Purpose
ping Health / Test Connection
capabilities.list Discover tools and business flows for Sync Tools
tools.list Legacy tool-only discovery (still supported)
tool.invoke Run a handler
tool.resume Continue after a customer challenge reply

Requests are HMAC-SHA256 signed (X-Qefro-Signature / X-Qefro-Timestamp, payload v1:<timestamp>:<body>). Responses include X-Qefro-Protocol, X-Qefro-SDK, and X-Qefro-Version.

Examples

export QEFRO_SIGNING_SECRET=dev-secret
python examples/basic/server.py            # ask -> tool -> complete
python examples/order-approval/server.py   # condition + approval + OTP-authenticated tool

Docs

Development

pip install -e ".[dev]"
pytest

License

MIT

Metadata

Release files for qefro-backend 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qefro-backend 1.3.0
File Size Uploaded
qefro_backend-1.3.0.tar.gz 39.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qefro-backend 1.3.0
File Interpreter ABI Platform
qefro_backend-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 87.6 kB

Release files / qefro_backend-1.3.0.tar.gz

Download URL qefro_backend-1.3.0.tar.gz
Size 39.9 kB
Tags Source
SHA-256 checksum
How to use checksums
b5666ae9c86d85322b8f10d406ffbebd421e812f39228951f85bb17383171245
BLAKE2b-256 checksum
How to use checksums
34d9d78a68d116eed32d7d7a63d837126e28336f7349362c84d7b7bb0b0f94c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / qefro_backend-1.3.0-py3-none-any.whl

Download URL qefro_backend-1.3.0-py3-none-any.whl
Size 47.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ff8869b165b6c32c1becff17674ff0065cf3050d936dadf51cbf8cedd942c58c
BLAKE2b-256 checksum
How to use checksums
0ee2e7e224e3854d00752c5e4ff0d038c6b22f15f9b2ec640d7ce7d73369b447
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

1.3.0 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page