Metadata-Version: 2.4
Name: conecto
Version: 0.1.0
Summary: Official Python SDK for Conecto — build chat bots, plugins and integrations on the Conecto customer-messaging platform.
Project-URL: Homepage, https://conecto.chat
Project-URL: Documentation, https://conecto.chat/developers
Project-URL: Source, https://github.com/Nancy-Consulting/conecto-sdk
Project-URL: Changelog, https://github.com/Nancy-Consulting/conecto-sdk/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/Nancy-Consulting/conecto-sdk/issues
Author-email: Conecto <developers@conecto.chat>
Maintainer-email: Conecto <developers@conecto.chat>
License: MIT
License-File: LICENSE
Keywords: ai agent,api,chatbot,conecto,customer support,helpdesk,integrations,live chat,plugins,sdk,webhooks
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: requests>=2.25
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: mypy>=1.5; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: anyio>=3.0; extra == 'fastapi'
Requires-Dist: fastapi>=0.100; extra == 'fastapi'
Provides-Extra: flask
Requires-Dist: flask>=2.0; extra == 'flask'
Description-Content-Type: text/markdown

# Conecto for Python

The official Python SDK for [Conecto](https://conecto.chat) — build chat bots, plugins
and integrations on top of a live customer-messaging platform.

```bash
pip install conecto
```

```python
from conecto import Conecto

client = Conecto()  # reads CONECTO_CLIENT_ID / CONECTO_SECRET

for convo in client.conversations.list(status="open"):
    print(convo.id, convo.preview)
```

Credentials come from the dashboard: **Settings → Developers → new credential**. The
secret is shown once.

---

## What you can build

### 1. Automate the workspace

Everything the platform does, driven from your code — conversations, contacts, tickets,
help articles, widget configuration, proactive messages.

```python
client.contacts.upsert("maya@acme.io", name="Maya", custom_fields={"plan": "pro"})

client.tickets.create(email="maya@acme.io", message="Card declined", priority="high")

client.visitors.message(
    widget_id=7, session=session_id,
    body="Your order shipped — want live tracking?",
    buttons=["Track my order"],
)
```

### 2. Build a bot

A bot is a webhook and a reply. Signature verification, delivery deduplication, event
routing and "don't answer your own messages" are handled; you write the part that is
yours.

```python
from conecto import Conecto, Bot, blocks

client = Conecto()
bot = Bot(client, secret=WEBHOOK_SECRET)

@bot.on_message
def handle(ctx):
    if "refund" in ctx.text.lower():
        ctx.reply(
            "I can start a refund. Which order?",
            buttons=["#1284", "Something else"],
        )
    elif "how do i reset" in ctx.text.lower():
        ctx.reply("Here's how:", blocks=[
            blocks.image("https://cdn.you.com/reset.gif", alt="Reset flow"),
            blocks.buttons([
                blocks.reply_button("That worked"),
                blocks.link_button("Full guide", "https://docs.you.com/reset"),
            ]),
        ])
    else:
        ctx.note("Bot didn't recognise this — handing over.")
        ctx.handoff()

# Flask
app.add_url_rule("/conecto", view_func=bot.flask_view(), methods=["POST"])
```

Adapters ship for **Flask**, **FastAPI**, **Django** and bare **WSGI**.

### 3. Build a plugin

Declare what *your* systems can do, and the AI agent calls them mid-conversation. Your
store, your billing, your CRM — whatever software they run on.

```python
from conecto import Conecto, Plugin, parameter

plugin = Plugin("acme-store", base_url="https://api.acme.com/conecto")

@plugin.action(
    "catalog.search_products",                       # a reserved name: results become
    description="Search the Acme catalog by keyword.",  # product cards automatically
    parameters=[parameter("query", type="string", required=True)],
)
def search(req):
    hits = catalog.search(req.arguments["query"], limit=4)
    if not hits:
        return req.not_found()
    return req.ok(products=[
        {"title": p.name, "url": p.url, "image": p.image,
         "price_from": f"{p.price:.2f}", "currency": "USD", "available": p.stock > 0}
        for p in hits
    ])

@plugin.action(
    "orders.get_status",
    risk="verified_read",                            # refused until identity is proven
    description="Status of one of the visitor's own orders.",
    parameters=[parameter("order_number", required=True)],
)
def order_status(req):
    order = orders.find(req.arguments["order_number"], email=req.verified_email)
    return req.ok(**order) if order else req.not_found()

app.add_url_rule("/conecto/<path:action>", view_func=plugin.flask_view(),
                 methods=["POST"])

plugin.deploy(Conecto())   # idempotent — run it on every release
```

That is a complete storefront integration. The product cards, the Home showcase and the
identity checks come with it.

---

## Rich content

Any message can carry images, GIFs, video, audio, files, embeds, card carousels,
label/value lists and buttons.

```python
from conecto import blocks

convo.reply("Here's your order:", blocks=[
    blocks.list_block([
        blocks.row("Placed", "12 July 2026"),
        blocks.row("Status", "Shipped"),
        blocks.row("Carrier", "DHL", subtitle="Tracking 4Z8871",
                   url="https://track.dhl.com/4Z8871"),
    ], title="Order #1042"),
    blocks.cards([
        blocks.card("Trail Runner 2", price="89.00 USD", badge="Back in stock",
                    image="https://cdn.you.com/tr2.jpg",
                    url="https://shop.you.com/p/tr2",
                    buttons=[blocks.link_button("Buy", "https://shop.you.com/cart")]),
    ]),
])
```

No public URL for that GIF? Upload it:

```python
media = client.media.upload("celebrate.gif")
convo.reply("All set!", blocks=[blocks.image(media)])
```

Every builder validates locally, so a typo raises `BlockError` from *your* line instead
of coming back as an HTTP 400.

---

## Identity

Actions that touch one person's data will not run until we have proven who they are —
by an emailed code, or because your site vouched for a logged-in user:

```python
# your backend, right after your own auth check
client.visitors.identify(
    widget_id=7, session=conecto_session,
    email=user.email, name=user.name,
    verified=True, verify_hours=24,
    data={"plan": user.plan},
)

# on logout
client.visitors.unverify(widget_id=7, session=conecto_session)
```

Inside a plugin, `req.verified_email` is the address we proved. An email in
`req.arguments` is whatever the model parsed out of a chat — never authorization.

---

## Things that will save you an afternoon

**Verify webhooks against the raw body.** Re-serializing JSON changes the bytes and the
signature stops matching. This is the most common first-integration bug, and the SDK
raises a message saying so if you pass a parsed dict.

```python
event = webhooks.parse(request.get_data(), request.headers, secret=SECRET)
```

**Deliveries are at-least-once.** Dedupe on `event.id`, and pass it as your idempotency
key when replying — `Bot` does both for you.

**Pagination iterates itself.**

```python
page = client.contacts.list(query="acme")
page.items      # this page
for c in page:  # every page, fetched lazily
    ...
page.all()      # everything, in one list
```

**Errors are typed.**

```python
from conecto import ConversationClosed, RateLimited

try:
    convo.reply("Refunded.")
except ConversationClosed:
    convo.reopen()
    convo.reply("Refunded.")
```

Retries for timeouts, 429s and 5xx are automatic and honour `Retry-After`. Writes are
retried safely because every write carries an idempotency key.

---

## Reference

| | |
|---|---|
| `client.conversations` | list, read, reply, typing, handoff, assign, close |
| `client.contacts` | upsert by email, search, update, delete |
| `client.visitors` | identify + vouch, unverify, proactive messages |
| `client.tickets` | create, update, reply, categories |
| `client.articles` | search and publish help-center content |
| `client.integrations` | register, install, run and deploy plugins |
| `client.widgets` | read and update widget configuration |
| `client.media` | upload files for blocks |
| `client.webhooks` | subscribe endpoints to events |
| `client.members` | teammates, for assignment |
| `client.meta` | `me()`, `schema()`, `stats()` |

`client.meta.schema()` returns the whole API described as JSON — endpoints, events, block
types, reserved actions and every limit — served by the same code that enforces them.

**Requirements:** Python 3.9+ and `requests`.

**Documentation:** <https://conecto.chat/developers> · **Issues:**
<https://github.com/Nancy-Consulting/conecto-sdk/issues>

MIT licensed.
