Skip to main content

woise (Python)

Voice and chat agents for your systems: your CRM, app or phone system places calls, runs campaigns, chats with agents, talks to them live, and gets the results back as webhooks.

pip install woise            # live audio: pip install "woise[live]"

Python 3.9+. One dependency (httpx). Woise is sync, AsyncWoise is the same API with await. The API key picks your region: sk-woise-us-... talks to https://us.woise.ai, sk-woise-in-... to https://in.woise.ai. Older keys without a region need Woise(key, region="us"). Local dev or a partner domain: Woise(key, base_url="http://localhost:8000").

Build an agent and place a call

import os
from woise import Woise, Agent

woise = Woise(os.environ["WOISE_API_KEY"])

agent = Agent("Smile Dental", prompt="You are Priya, the receptionist at Smile Dental.")
agent.start("greet", say="Hi, this is Priya from Smile Dental.", prompt="Confirm tomorrow's visit.",
            next={"done": "they confirmed or cancelled"})
agent.end("done", say="Thanks, bye.")
agent.after_call(collect={"outcome": "confirmed, cancelled or rescheduled"})
saved = woise.agents.save(agent, publish=True)          # save again later = new draft

call = woise.calls.create(agent=saved, to="+14155550123", variables={"first_name": "Ravi"})
print(call.status, call.id)                              # queued, enr_...

next maps the step to go to, to the condition in plain words. agent.anytime("human", when="asks for a person", transfer="+14155550100") adds a step that can fire from anywhere. agent.node(id, type, data) adds any other node.

Run a campaign from a CSV

# leads.csv: phone_number,first_name,plan   (every other column becomes a {{variable}})
campaign = woise.campaigns.create(agent=saved, contacts="leads.csv", number="+14155550100")
campaign.launch()
print(campaign.progress())
campaign.pause(); campaign.resume()
campaign.add_contacts([{"phone_number": "+14155550124", "first_name": "Sita"}])

Get results back: call.ended in Flask, with the signature check

hook = woise.webhooks.create(url="https://crm.example.com/woise", events=["call.ended"])
print(hook.secret)                                       # shown ONCE: store it as WOISE_WEBHOOK_SECRET
from flask import Flask, abort, request
from woise import verify_signature

app = Flask(__name__)

@app.post("/woise")
def woise_event():
    if not verify_signature(request.get_data(), request.headers.get("X-Woise-Signature"),
                            os.environ["WOISE_WEBHOOK_SECRET"]):
        abort(401)
    event = request.get_json()
    print(event["event"], event["call_id"], event["call_disposition"], event["collected"])
    return "", 204

Events: call.started, call.ended, chat.ended, campaign.finished. Failed deliveries are retried with backoff; woise.webhooks.deliveries(hook) shows the log.

Chat

chat = woise.chats.start(saved, variables={"first_name": "Ravi"})
print(chat.greeting)
print(chat.send("Are you open on Sunday?").text)
chat.end()

Runs: transcripts, outcomes, recordings

for run in woise.runs.list(agent=saved, limit=20):
    print(run.id, run.outcome, run.collected)
    print(run.transcript)                                # "agent: ...\ncaller: ..."
print(woise.runs.recording_url(run))                     # short-lived link

Live voice: Twilio Media Streams -> Woise in ~20 lines

Point a Twilio <Connect><Stream url="wss://you.example.com/twilio"/></Connect> at this relay. It forwards Twilio's messages to Woise verbatim and Woise answers in the same format (media, mark, clear), so barge-in works unchanged.

import asyncio, json, os
import websockets
from woise import AsyncWoise

woise = AsyncWoise(os.environ["WOISE_API_KEY"])
AGENT = "wf_..."                                          # your agent id

async def relay(twilio):
    async with woise.live.voice(AGENT, format="twilio") as call:
        async def up():                                   # Twilio -> Woise
            async for message in twilio:
                await call.send_raw(message)
        async def down():                                 # Woise -> Twilio
            async for event in call:
                await twilio.send(json.dumps(event.data))
        await asyncio.gather(up(), down())

async def main():
    async with websockets.serve(relay, "0.0.0.0", 8080):
        await asyncio.Future()

asyncio.run(main())

Raw PCM16 instead (your own media server, 8/16/24 kHz mono):

with woise.live.voice(AGENT, sample_rate=16000) as call:
    call.send_audio(pcm16_bytes)
    for event in call:
        if event.type == "audio": speaker.write(event.audio)
        elif event.type == "interrupt": speaker.flush()     # the caller barged in
        elif event.type == "transcript": print(event.data["role"], event.text)

woise.live.chat(agent) is the same socket for text: send_text, then stream agent_text events.

More

  • Numbers: woise.numbers.connections(), .carrier_numbers(conn), .import_byo(conn, ["+1..."]), .list(connection_id=conn), .attach(number, agent, connection_id=conn), .detach(number, connection_id=conn) (managed numbers: .list(), .attach(number, agent))
  • woise.tools.create_http("lookup_patient", url="https://crm.example.com/patients", parameters={"phone": "the caller's number"})
  • woise.knowledge.add("faq.pdf")
  • Errors are WoiseError with .status, .code, .message, .request_id. GET/PUT/DELETE are retried on 429/5xx; calls.create never is (pass idempotency_key= to retry safely).

BSD-2-Clause.

Metadata

Release files for woise 1.0.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 woise 1.0.0
File Size Uploaded Provenance Actions
woise-1.0.0.tar.gz 28.0 kB Without provenance Details for woise-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for woise 1.0.0
File Interpreter ABI Platform Actions
woise-1.0.0-py3-none-any.whl Python 3 none any Details for woise-1.0.0-py3-none-any.whl

Total release size: 49.4 kB

Release files / woise-1.0.0.tar.gz

Download URL woise-1.0.0.tar.gz
Size 28.0 kB
Tags Source
SHA-256 checksum
How to use checksums
f99a310b4365cd7b460e9acddbf33c01ed016fe614e80a0601b40cbedab8d153
BLAKE2b-256 checksum
How to use checksums
215274a722c090ddd8708f53ff7869891121b253aeb46139cbe40da63bfe308b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / woise-1.0.0-py3-none-any.whl

Download URL woise-1.0.0-py3-none-any.whl
Size 21.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1bc83061b04ff6122b4a7b3b1de104a16918adf221b0037218763f659a62c2dc
BLAKE2b-256 checksum
How to use checksums
7d79dc734f12194422d580d8c508b2b24ab0d326646a3fa915f717f0ae4c79da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Security for woise 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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