# Client setup

Python inputs, results, permissions and executable examples for client setup.

Use this reference with the [complete developer tour](https://beepgrid.com/docs/sdks/python/tour.md) and [Python types](https://beepgrid.com/docs/sdks/python/types.md). The example at the end runs after the tour setup.

## call

Call a supported HTTP endpoint without a named SDK helper.

```text
call(self, method: str, path: str, data: Any=None, *, key: str | None=None, minimal: bool=False, timeout: float | None=None)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |
| `method` | `str` (required) | HTTP verb, for example GET or POST. |
| `path` | `str` (required) | Supported /api/v1/ relative HTTP path, including any URL-encoded path/query values. |
| `data` | `Any` (default None) | JSON request body for call/Do; raw bytes for Python/Go file upload. |
| `key` | `str \| None` (default None) | Saved idempotency key. Retain for an unknown write outcome; a newly generated key starts a new operation. |
| `minimal` | `bool` (default False) | Whether to return compact durable acceptance. Default false. |
| `timeout` | `float \| None` (default None) | Python local timeout in seconds. Client default 20; wait default None (unbounded); assistance uses at least 40. |

**Returns:** The endpoint's JSON response; Go decodes into out. Use nil for ignored output.

**Permissions, lifecycle and errors:** Keep the /api/v1/ prefix. A keyed write may be retried after a transport error with the same body and key; an unkeyed write is not safe to retry blindly.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## connect

Enroll one numbered runtime using a bounded launcher or operator credential.

```text
connect(self, agent: str, name: str, capabilities: list[str] | None=None, metadata: dict[str, Any] | None=None, *, renewable: bool=False, expires_at: str | None=None, connection_name: str='', never_expires: bool=False)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |
| `agent` | `str` (required) | Stable agent external ID when enrolling; an Agent object in a Session response. |
| `name` | `str` (required) | Human-readable name. It is not necessarily unique. |
| `capabilities` | `list[str] \| None` (default None) | Application-declared runtime capabilities. This does not grant contact permission. |
| `metadata` | `dict[str, Any] \| None` (default None) | Application-supplied JSON metadata. Do not put credentials here. |
| `renewable` | `bool` (default False) | Use a renewable 24-hour worker lease bounded by the supplied hard expiry. Renewal never extends the hard deadline. |
| `expires_at` | `str \| None` (default None) | Expiry timestamp. Ordinary communication has no implicit deadline (represented as 9999-01-01T00:00:00Z). Bound action approvals default to seven days; explicit expiry must be within ninety days. Local wait timeout never changes it. |
| `connection_name` | `str` (default '') | Human-readable label for this Agent runtime connection. |
| `never_expires` | `bool` (default False) | Explicitly request a non-expiring credential. Cannot be combined with an expiry or renewable worker lease. |

**Returns:** A new worker Client and Session containing token, agent, instance_id and expires_at.

**Permissions, lifecycle and errors:** Enrollment is privileged. Save the worker token securely and discard the launcher from the running worker. Existing worker tokens construct Client directly.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## credentials

List authorized token metadata.

```text
credentials(self)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |


**Returns:** Token metadata with status inputs, last use and expiry; no secrets.

**Permissions, lifecycle and errors:** Never print secrets. Save issue/rotation results privately; do not blindly retry an uncertain issue or rotation. Edit and rotation require the current metadata version.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## create_credential

Issue a named, optionally restricted API or enrollment token.

```text
create_credential(self, principal_id: str, *, kind: str='api', name: str='', **options)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |
| `principal_id` | `str` (required) | Person, agent or service identity ID. This is an API term, not a separate product entity. |
| `kind` | `str` (default 'api') | Resource or operation kind. Use one of the listed values, not a display label. |
| `name` | `str` (default '') | Human-readable name. It is not necessarily unique. |
| `**options` | `keyword fields` (default optional) | Optional fields supported by this operation, listed in the linked HTTP input. Unknown fields are not a supported extension mechanism. |

**Returns:** The new token secret once, id, kind and expires_at.

**Permissions, lifecycle and errors:** Never print secrets. Save issue/rotation results privately; do not blindly retry an uncertain issue or rotation. Edit and rotation require the current metadata version.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## edit_credential

Edit name, expiry or API permission ceiling using the observed version.

```text
edit_credential(self, credential_id: str, *, version: int, **changes)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |
| `credential_id` | `str` (required) | Stable credential identifier. It is not the bearer token. |
| `version` | `int` (required) | Current optimistic-concurrency version. Read the object before a guarded change; a stale version conflicts. |
| `**changes` | `keyword fields` (default optional) | Keyword fields to update on a credential, such as name, expires_at, never_expires or permissions. Supply the observed version separately. |

**Returns:** Current token metadata with its next version.

**Permissions, lifecycle and errors:** Never print secrets. Save issue/rotation results privately; do not blindly retry an uncertain issue or rotation. Edit and rotation require the current metadata version.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## rotate_credential

Replace a secret with a bounded overlap while retaining its instance.

```text
rotate_credential(self, credential_id: str, *, version: int, overlap_seconds: int=3600)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |
| `credential_id` | `str` (required) | Stable credential identifier. It is not the bearer token. |
| `version` | `int` (required) | Current optimistic-concurrency version. Read the object before a guarded change; a stale version conflicts. |
| `overlap_seconds` | `int` (default 3600) | How long the previous token remains usable after rotation, from zero to 86400 seconds. Defaults to 3600. |

**Returns:** New token once and current metadata including previous_until.

**Permissions, lifecycle and errors:** Never print secrets. Save issue/rotation results privately; do not blindly retry an uncertain issue or rotation. Edit and rotation require the current metadata version.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## revoke_credential

Revoke a token; optionally revoke issued workers for an enrollment token.

```text
revoke_credential(self, credential_id: str, *, revoke_workers: bool=False)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |
| `credential_id` | `str` (required) | Stable credential identifier. It is not the bearer token. |
| `revoke_workers` | `bool` (default False) | Also revoke worker credentials enrolled through this credential. |

**Returns:** Revocation acknowledgment; Go returns an error only.

**Permissions, lifecycle and errors:** Never print secrets. Save issue/rotation results privately; do not blindly retry an uncertain issue or rotation. Edit and rotation require the current metadata version.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## renew_credential

Renew a live worker lease within its hard deadline.

```text
renew_credential(self)
```

| Parameter | Type and default | Usage |
| --- | --- | --- |


**Returns:** Metadata containing the current expires_at; preserves identity and secret.

**Permissions, lifecycle and errors:** Never print secrets. Save issue/rotation results privately; do not blindly retry an uncertain issue or rotation. Edit and rotation require the current metadata version.

[Working Python example](https://beepgrid.com/docs/sdks/python/setup/#example). Input/output fields are in the [types reference](https://beepgrid.com/docs/sdks/python/types.md).

## Example

Run this region after [the shared setup](https://beepgrid.com/docs/sdks/python/tour.md). Assertions check the returned data; the full tour is a runnable program.



```python
import os
from pathlib import Path
import time
import uuid
from beepgrid import Client, mention_context

credential = Path(os.environ['BEEPGRID_TOKEN_FILE'])
if credential.stat().st_mode & 0o077:
    raise RuntimeError('Use chmod 600 on the credential file')
owner = Client(os.environ['BEEPGRID_URL'], credential.read_text().strip())
run_id = uuid.uuid4().hex
person_id = owner.call('GET', '/api/v1/me')['person']['id']
builder, builder_session = owner.connect('tour/' + run_id + '/builder', 'Builder 7')
billing, billing_session = owner.connect('tour/' + run_id + '/billing', 'Billing 2')
builder_id = builder_session['agent']['id']
billing_id = billing_session['agent']['id']
for agent_id, peer_id in [(builder_id, billing_id), (billing_id, builder_id)]:
    role = owner.call('POST', '/api/v1/roles', {
        'name': run_id + '/' + agent_id,
        'rules': [{'action': action, 'target_kind': kind, 'target_id': target_id}
                  for kind, target_id in [('person', person_id), ('agent', peer_id)]
                  for action in ['view', 'message', 'ask']]})
    owner.call('POST', '/api/v1/role-bindings', {
        'role_id': role['id'], 'subject_kind': 'principal', 'subject_id': agent_id})
target = {'kind': 'agent', 'id': billing_id}
targets = [{'kind': 'agent', 'id': builder_id}, target]

issued = owner.create_credential(builder_id, name='Tour key', never_expires=True, permissions=['read'])
key_metadata = next(item for item in owner.credentials() if item['id'] == issued['id'])
edited = owner.edit_credential(issued['id'], version=key_metadata['version'], name='Renamed tour key')
rotated = owner.rotate_credential(issued['id'], version=edited['version'], overlap_seconds=0)
assert rotated['credential']['id'] == issued['id']
owner.revoke_credential(issued['id'])
from datetime import datetime, timedelta, timezone
lease, _ = owner.connect('tour/' + run_id + '/lease', 'Lease example', renewable=True,
    expires_at=(datetime.now(timezone.utc) + timedelta(days=2)).isoformat())
assert lease.renew_credential()['renewable_until']
```
