Metadata-Version: 2.4
Name: qefro-backend
Version: 1.1.0
Summary: Qefro backend framework for business tool handlers and customer authorization orchestration
Project-URL: Homepage, https://docs.qefro.com/docs/guides/register-sdk-business-tools
Project-URL: Documentation, https://docs.qefro.com/docs/guides/define-business-flows
Project-URL: Repository, https://github.com/qefro-ai/qefro-python-backend-sdk
Project-URL: Issues, https://github.com/qefro-ai/qefro-python-backend-sdk/issues
Author: Qefro
License: MIT
License-File: LICENSE
Keywords: ai,business-tools,chat,customer-support,qefro,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# Qefro Python Backend SDK

`qefro-backend` is the Python framework for building Qefro **Business Tool**
handlers and **Business Flows**. Register your tools and flows, serve the signed
Qefro webhook, and let the Qefro Runtime orchestrate everything — including
multi-step flows and customer verification.

Wire-compatible with the [JavaScript](https://github.com/qefro-ai/qefro-js-backend-sdk)
and [Rust](https://github.com/qefro-ai/qefro-rust-backend-sdk) SDKs: same signed
protocol, same `capabilities.list` schema, same 8 flow step types.

## Install

```bash
pip install qefro-backend
```

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

## Quick start

```python
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.

```python
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.

```python
@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` (when present in your stack) remains independent — Hub is never
the sole path. External CRM auth via `app.customer(provider)` is unchanged.

## 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.

```python
(
    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()`. A duplicate/empty flow or step id raises
`FlowError`. See [`examples/order-approval`](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

```bash
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

- [Register SDK Business Tools](https://docs.qefro.com/docs/guides/register-sdk-business-tools)
- [Define Business Flows](https://docs.qefro.com/docs/guides/define-business-flows)
- [Run Business Flows](https://docs.qefro.com/docs/guides/run-business-flows)

## Development

```bash
pip install -e ".[dev]"
pytest
```

## License

MIT
