Metadata-Version: 2.4
Name: actionbox-sdk
Version: 0.1.6
Summary: Python client for Actionbox durable human decisions
License-Expression: MIT
Project-URL: Documentation, https://actionbox.cloud/docs
Project-URL: API, https://api.actionbox.cloud/docs
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Dynamic: license-file

<img src="https://actionbox.cloud/appbox.svg" width="64" alt="Actionbox logo">

# Actionbox Python SDK

Actionbox gives backend services a durable, server-authoritative way to ask a
human for a decision and continue when that decision is available. This package
is the typed Python client for creating, resolving, and waiting on Actions,
plus managing source-scoped heartbeat Watches.

## Documentation

- [Actionbox documentation](https://actionbox.cloud/docs)

## Requirements

- Python 3.11 or newer
- An Actionbox Source API key, supplied through `ACTIONBOX_API_KEY`

Keep API keys and Watch capability URLs on trusted servers, workers, or CI
jobs. Do not put this SDK or its credentials in browser code.

## Install

```bash
pip install actionbox-sdk
```

The distribution is named `actionbox-sdk` so it does not conflict with the
Actionbox CLI distribution on PyPI. The Python import remains `actionbox`.

```python
import os
from actionbox import Actionbox

with Actionbox(os.environ["ACTIONBOX_API_KEY"]) as client:
    decision = client.ask(
        title="Deploy to production?",
        options=["Approve", "Reject"],
        callback_url="https://ci.example.com/actionbox",
    )
    print(decision)
```

The SDK uses the hosted production API at `https://api.actionbox.cloud` by
default. Customer integrations should use that default and only pass
`base_url` in maintainer-controlled test environments.

`ask(..., wait=False)` returns an `Action`; `Action.wait()` polls the server and leaves the Action open when the local timeout expires. The concise single-choice API returns the selected option ID as a string.

## Typed interactions and responses

The SDK exports typed interaction and response contracts that match the REST API. Use an explicit typed interaction with `create` or `ask` when the human response is more than a single choice:

```python
from actionbox import Actionbox, BooleanInteraction

with Actionbox(os.environ["ACTIONBOX_API_KEY"]) as client:
    action = client.create(
        title="Deploy configuration",
        interaction=BooleanInteraction(
            type="boolean",
            label="Deploy now?",
            true_label="Deploy",
            false_label="Hold",
        ),
    )
    resolved = client.resolve(
        action.id,
        response={"type": "boolean", "value": True},
        reason="Approved by release manager",
    )
    print(resolved.response)  # {"type": "boolean", "value": True}
```

The available interaction types are `boolean`, `single_choice`, `multi_choice`, `text`, `integer`, `number`, `rating`, and `form`. Form fields use the same typed field shapes and are returned as `{"type": "form", "values": {...}}`. Create inputs also accept bounded developer `context` blocks and an explicit typed `on_expire` fallback; omitting it returns `expired` without inventing a response.

`resolve` supports concise single-choice syntax and generic typed input:

```python
client.resolve(action.id, "approve")  # single-choice shorthand
client.resolve(action.id, {"response": {"type": "text", "value": "ship"}})
client.actions.resolve(action.id, response={"type": "number", "value": 4.5})
```

`Action.interaction` and `Action.response` expose the canonical typed wire values. `options`, `option_id`, `ask(..., options=[...])`, and string decision results are first-class single-choice conveniences.

## Execution outcomes

After carrying out an approved operation, report its real result from the
resolved Action snapshot:

```python
outcome = resolved.report_outcome(
    "success",
    duration_ms=48_312,
    rollback=False,
)
```

The SDK sends the Action's exact version and fingerprint. Exact retries are
safe; Actionbox rejects a conflicting second outcome.

## Agent Runs

Group an agent task and its Actions without managing another framework:

```python
run = client.runs.start(
    external_id="checkout-fix-42",
    agent_name="codex",
    title="Fix checkout deadlock",
    stall_after_seconds=900,
)
run.progress(stage="tests", checkpoint="test-184")
action = client.create(title="Approve staging migration", run_id=run.id)
run.complete()
```

The SDK handles progress sequence numbers. Runs are optional; standalone
Actions continue to work exactly as before. When `stall_after_seconds` is set,
unchanged status/stage/checkpoint updates do not reset the timer. Actionbox
creates one ordinary Action if progress stalls and resolves it when progress
changes or the Run completes; `waiting` pauses the timer.

## Heartbeat Watches

Source credentials can create and list Watches scoped to that Source. The raw heartbeat URL is returned only by creation:

```python
from actionbox import Actionbox, send_heartbeat

with Actionbox(os.environ["ACTIONBOX_API_KEY"]) as client:
    watch = client.watches.create(
        source_id="src_…",
        name="Nightly backup",
        schedule_type="interval",
        interval_seconds=3600,
        grace_seconds=60,
        signal_method="post",
    )
    send_heartbeat(watch.heartbeat_url, "start")
```

Use `send_heartbeat` for `ping`, `start`, `success`, or `fail`; it always sends
POST. `signal_method="post"` prevents link previewers and security scanners
from accidentally recording a heartbeat with GET. The
source-scoped resource also exposes `client.watches.pause(id)`,
`resume(id)`, `rotate_token(id)`, and `archive(id)`; only create/rotate return
a raw URL. Store capability URLs in a secret manager; Watch details and
exports never return them.

## License

MIT. See [LICENSE](./LICENSE).
