Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# Changelog

## Unreleased

- Clarify request-level webhook configuration and purchased-number requirements for batch calls.

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [1.0.0] - 2026-09-23

### Changed

- Clarify placeholder API keys and redaction of credentials and private call data in SDK documentation.
- Move `client.calls` to the single-target Calls API with required phone, result schema and idempotency key.
- Support optional region/language hints, cancellation before submission, and detailed call events.
- Expose Billing call ID, call outcome, result readiness and recorded transcript turns.
- Stop Calls and Goal Run wait helpers when result readiness is final, including unavailable results with no error.
- This is a breaking Calls migration. Retain SDK 0.7.x for historical legacy call-task IDs; see the public migration guide.
- Clarify webhook configuration, legacy batch-call requirements, and redaction of credentials and private call data.

## [0.7.1] - 2026-09-04

Expand Down
3 changes: 1 addition & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,13 @@ In scope:
- List call events.
- List and read published Goals.
- Create a Goal Run with a durable idempotency key.
- Poll until a Goal Run has either a result or an error.
- Poll until a Goal Run's `result_status` is no longer `pending`.
- Receive finalized terminal webhook events without requiring signature
material.

Out of scope:

- Async client support.
- Cancel calls.
- Recurring or scheduled calls.
- Goal authoring and publishing.
- Project-level webhook management.
Expand Down
84 changes: 53 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ Python server SDK for the CALL-E Developer API.
Use this SDK from backend services, workers, and other trusted server
environments. Do not expose CALL-E API keys in browser code.

SDK 1.0 uses the single-target Calls API and the same result model as Goal Runs. A required
closed scalar-object result schema defines the business fields. SDK wait helpers
continue while `result_status` is `pending`, even after execution reaches `completed`.
Empty `{}` is a ready result. Webhook data matches the persisted GET snapshot.

## Documentation

- Developer docs: <https://docs.heycall-e.com/>
Expand All @@ -32,7 +37,7 @@ environments. Do not expose CALL-E API keys in browser code.
Install the stable package from PyPI:

```bash
pip install calle-ai
pip install calle-ai==1.0.0
```

For reproducible deployments, pin the package version selected by your
Expand Down Expand Up @@ -194,47 +199,64 @@ else:
```

Persist the idempotency key before the first request and reuse it for network
retries. `wait_for_result` returns when either `result` or `error` is non-null;
retries. `wait_for_result` returns when `result_status` is no longer `pending`;
an execution `status` of `completed` can still be waiting for result
materialization.

The generic one-shot call API remains available independently:

```python
import os
from calle import CalleClient
## Migration to SDK 1.0

client = CalleClient(
api_key=os.environ["CALLE_API_KEY"],
base_url="https://api.heycall-e.com",
)
The `calls` wrapper now submits one explicit phone to `/v2/calls` and requires
an idempotency key. Keep the key for retries. The backend continues serving
legacy integrations until their planned retirement at the end of 2026; keep
SDK 0.7.x for historical legacy call IDs. Upgrade Goal Run integrations to 1.0
as well: old wait helpers can time out when both `result` and `error` are null.

```python
call = client.calls.create_and_wait(
task="Call each recipient and ask whether they can attend Friday lunch in San Francisco.",
recipients=[{"phones": ["+14155550100"], "region": "US", "locale": "en-US"}],
task="Ask whether Friday lunch is confirmed.",
phone="<AUTHORIZED_E164_PHONE>", region="US", locale="en-US",
result_schema={
"type": "object",
"required": ["completed_count"],
"properties": {
"completed_count": {"type": "integer"},
},
},
recipient_result_schema={
"type": "object",
"required": ["can_attend"],
"properties": {
"can_attend": {"type": "string", "enum": ["yes", "no", "unknown"]},
},
"type": "object", "additionalProperties": False, "required": ["answer"],
"properties": {"answer": {"type": "string", "enum": ["yes", "no", "unknown"]}},
},
metadata={"workflow_run_id": "wf_123"},
idempotency_key="wf_123_friday_lunch",
idempotency_key="lunch:friday:confirmation:v1",
)

print(call["status"], call["structured_result"])
print(call["task_completed"], call["completion_confidence"], call["evidence"])
print(call["recipients"][0]["structured_result"])
if call["error"] is None:
print(call["result"])
else:
print(call["error"])
# Cancel a queued call before provider submission:
# client.calls.cancel(call_id)
```

Replace `recipient` / `recipients` with `phone` and optional `region` and `locale`.
Define the required `result_schema` (`resultSchema` in TypeScript) using Goal's
`calle.result.scalar-object.v1` profile: at most 32 scalar properties and
`additionalProperties: false`. Flatten old nested fields; arrays and null values
are unsupported. Old `structured_result`, `result_error`, summary,
confidence and provider attempt fields are removed. Request business summaries or
completion flags explicitly as scalar fields in the schema when needed.
The Calls API does not support batch, scheduled or recurring calls.

Calls and Goal Runs always expose `transcript`, an array of recorded turns with
`speaker` (`bot`, `user`, `unknown`), nullable `offset_seconds`, and `text`.
Before execution ends or without available transcript, the array is empty.
A pending, unavailable or failed business result does not remove an available
terminal transcript. Webhook data uses the same shape.

The returned object is `call`. Consume `result` and `error` exactly as for Goal Runs.
Poll while result_status is pending (resultStatus in TypeScript). No-answer, busy
and declined are ordinary call_outcome values (callOutcome in TypeScript).
Unavailable business evidence uses result_status=unavailable and error=null.
Explicit schema-valid task fallbacks remain results. Technical failures use error.
Cancellation uses result_status=not_applicable and no error. GET reads committed state and
terminal webhooks contain the same ready snapshot. Deduplicate by event id.

Cancellation returns `409 call_cannot_cancel` after provider submission begins.
Calls accept immediate execution only. If authorization expires after submission,
`error.detail_code=authorization_expired` means the provider may still complete the call; do not
create an automatic replacement call.

## Error handling

The SDK exports errors for API responses, authentication, rate limits,
Expand Down
19 changes: 4 additions & 15 deletions examples/create_and_wait.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,12 @@ def main() -> None:
)

call = client.calls.create_and_wait(
task="Call each recipient and ask whether they can attend Friday lunch in San Francisco.",
recipients=[
{
"phones": [os.environ.get("CALLE_EXAMPLE_PHONE", "+14155550100")],
"region": "US",
"locale": "en-US",
}
],
task="Call the recipient and ask whether they can attend Friday lunch in San Francisco.",
phone=os.environ.get("CALLE_EXAMPLE_PHONE", "+14155550100"),
region="US", locale="en-US",
result_schema={
"type": "object",
"required": ["completed_count"],
"properties": {
"completed_count": {"type": "integer"},
},
},
recipient_result_schema={
"type": "object",
"additionalProperties": False,
"required": ["can_attend"],
"properties": {
"can_attend": {"type": "string", "enum": ["yes", "no", "unknown"]},
Expand Down
7 changes: 2 additions & 5 deletions examples/webhook_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,8 @@ def do_POST(self) -> None:
"Call completed",
{
"call_id": call_id,
"result": call.get("structured_result"),
"summary": call.get("summary"),
"task_completed": call.get("task_completed"),
"completion_confidence": call.get("completion_confidence"),
"evidence": call.get("evidence"),
"result": call.get("result"),
"error": call.get("error"),
},
)
else:
Expand Down
2 changes: 1 addition & 1 deletion openapi-python-client.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
project_name_override: calle-generated
package_name_override: generated
package_version_override: 0.7.1
package_version_override: 1.0.0
literal_enums: true
generate_all_tags: true
use_path_prefixes_for_title_model_names: false
Expand Down
Loading
Loading