Playbook Management
Methods for reviewing, searching, retrieving, adding, and deleting user playbooks and agent playbooks.
Durable aggregation operations
Use submit_playbook_aggregation with request_id and agent_version for a full
aggregation that may outlive the HTTP request. Admission returns a persisted
receipt; it does not wait for generation. SQLite with its vector extension,
managed Supabase, and native PostgreSQL support this API. Storage adapters without
durable-operation support return HTTP 503 rather than accepting work they cannot
execute.
operation = client.submit_playbook_aggregation(
request_id="aggregate-v2-release-1", agent_version="v2"
)
operation = client.get_playbook_aggregation_operation(operation.operation_id)
print(operation.status, operation.result)Use get_playbook_aggregation_operation with the returned operation ID to poll.
If submission times out, repeat it with the same request ID and agent version. It returns the same operation, including after completion. Reusing that ID with different parameters returns HTTP 409. A different request ID also returns 409 while an operation for that agent version is unfinished. Managed requests are scoped by the authenticated organization and project; another project's receipt cannot be read with its operation ID.
The corresponding HTTP routes are POST /api/playbook_aggregation_operations
(HTTP 202, JSON request_id and agent_version) and
GET /api/playbook_aggregation_operations/{operation_id} (HTTP 200, or 404 for
an unknown receipt). Receipt timestamps are Unix seconds. Status is queued,
running, retrying, succeeded, or failed.
succeeded means the aggregation output and completion receipt committed
together, including valid zero-output results. Generated agent playbooks still
require their normal review and approval; optional tagging and optimization may
continue afterward. Workers recover unfinished operations after lease expiry,
with bounded retries for execution failures. Deleting or erasing current source
content cancels unfinished operations. Retiring captured inputs prevents the old
worker from saving its result; a retry uses the remaining current inputs.
Historical tombstone cleanup does not cancel work. Failed receipts expose a safe
error code, not provider messages or source content.
The existing synchronous aggregation API remains available. See receipt schemas.
search_user_playbooks
Search for user playbooks using semantic/text search and advanced filtering. On authenticated requests, served results are synchronously recorded as evidence before a successful response returns. If recording fails, the search fails; empty results create no evidence batch.
response = client.search_user_playbooks(
query="user satisfaction",
user_id="user_123",
request_id="request_123",
session_id="session_123",
agent_version="v2.1.0",
source="api",
tags=["support"],
top_k=10
)curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/search_user_playbooks" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"query": "user satisfaction",
"user_id": "user_123",
"request_id": "request_123",
"session_id": "session_123",
"agent_version": "v2.1.0",
"source": "api",
"tags": ["support"],
"top_k": 10
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
query? | str | — | Query for semantic/text search |
user_id? | str | — | Filter by user and identify the user for retrieval-experiment assignment. Required for agent traffic while an experiment is active. |
request_id? | str | — | Optional caller correlation ID for the search turn, at most 255 characters. |
session_id? | str | — | Optional caller session ID for the search turn, at most 255 characters. |
agent_version? | str | — | Filter by agent version |
playbook_name? | str | — | Filter by playbook name |
source? | str | — | Filter by exact interaction source. Omit or pass an empty string to disable source filtering. |
start_time? | datetime | — | Filter by start time |
end_time? | datetime | — | Filter by end time |
status_filter? | list[Status] | — | Filter by status (None for CURRENT, PENDING, ARCHIVED) |
tags? | string[] | — | Optional tag filter. Matches user playbooks that have any requested tag. |
top_k? | int | 10 | Maximum number of results to return, from 1 through 100. |
threshold? | float | model-specific | Similarity threshold for vector search. Omit it to use the embedding model's default. |
enable_reformulation? | bool | false | Enable query reformulation for improved search results |
search_mode? | SearchMode | — | Search mode: "vector", "fts", or "hybrid". When unset, the server uses its configured default ("hybrid"). |
review_user_playbooks
Re-run the evidence-grounded reviewer over the newest current user playbooks
created in an inclusive time window. top_k is applied in newest-first order.
For each selected playbook, the server loads the full interaction window recorded
by its finalized playbook-extraction run, adds any extra cited interactions
retained through consolidation, and restores request grouping. Historical review
is therefore not limited by the playbook's smaller cited-evidence subset, the
current extraction window size, or the current source filter. Only cited IDs are
presented as candidate evidence; the remaining generation-window interactions
provide chronology without becoming additional evidence.
The default report mode runs inline and returns decisions without changing
storage, while simulating the same newest-first context transitions as apply
mode. Because each selected playbook receives a fresh model review, the server
automatically gives this endpoint its extended synchronous timeout. A playbook
is reported as skip when its finalized generation-window provenance, validated
evidence metadata, or a required interaction/request is unavailable, or when its
ownership provenance does not match the requesting user; the run then continues.
The automatic reviewer that runs immediately
after generation separately uses that generation's configured extraction window.
Set report_only=False to apply the decisions. Apply mode is accepted and run
in the background — the response returns immediately with a run_id and an
empty results list, because a run makes one model call and one write per
selected playbook. The run reviews newest-first and commits each completed
decision before reviewing the next playbook: accepted rows stay current,
rejected rows are archived, and an edit inserts the replacement as current while
superseding its incumbent. A later failure stops the run but leaves earlier
decisions committed. Applied edits are recorded on the replacement's lineage
under the returned run_id.
from datetime import UTC, datetime
response = client.review_user_playbooks(
start_time=datetime(2026, 7, 1, tzinfo=UTC),
end_time=datetime(2026, 7, 28, tzinfo=UTC),
top_k=50,
report_only=True,
)
for result in response.results:
print(result.user_playbook_id, result.decision, result.reason)curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/review_user_playbooks" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "User-Agent: my-agent-reflexio" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"start_time": "2026-07-01T00:00:00Z",
"end_time": "2026-07-28T23:59:59Z",
"top_k": 50,
"report_only": true
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
start_time | datetime | — | Inclusive lower bound for user playbook creation time |
end_time | datetime | — | Inclusive upper bound for user playbook creation time; must be after start_time |
top_k? | int | 10 | Maximum number of newest matching current playbooks to review (1-100) |
report_only? | bool | true | Return decisions inline without writes when true; accept the run and apply it in the background when false |
See ReviewUserPlaybooksResponse for result fields and decision details.
search_agent_playbooks
Search for agent playbooks using semantic/text search and advanced filtering.
The optional source filter matches provenance: an agent playbook is included
when at least one linked source user playbook has that exact source.
response = client.search_agent_playbooks(
query="concise responses",
user_id="user_123",
agent_version="v2.1.0",
source="api",
tags=["tone"],
playbook_status_filter="approved"
)curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/search_agent_playbooks" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"query": "concise responses",
"user_id": "user_123",
"agent_version": "v2.1.0",
"source": "api",
"tags": ["tone"],
"playbook_status_filter": "approved"
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
query? | str | — | Query for semantic/text search |
user_id? | str | — | User receiving the playbooks. Used only for retrieval-experiment assignment, not as an agent-playbook filter. Required for agent traffic while an experiment is active. |
agent_version? | str | — | Filter by agent version |
playbook_name? | str | — | Filter by playbook name |
source? | str | — | Match agent playbooks linked to at least one user playbook with this exact interaction source. Omit or pass an empty string to disable source filtering. |
start_time? | datetime | — | Filter by start time |
end_time? | datetime | — | Filter by end time |
status_filter? | list[Status] | — | Filter by lifecycle status. Defaults to CURRENT and PENDING when omitted. |
tags? | string[] | — | Optional tag filter. Matches agent playbooks that have any requested tag. |
playbook_status_filter? | PlaybookStatus | — | Filter by playbook approval status (PENDING, APPROVED, REJECTED) |
top_k? | int | 10 | Maximum number of results to return |
threshold? | float | model-specific | Similarity threshold for vector search. Omit it to use the embedding model's default. |
enable_reformulation? | bool | false | Enable query reformulation for improved search results |
search_mode? | SearchMode | — | Search mode: "vector", "fts", or "hybrid". When unset, the server uses its configured default ("hybrid"). |
get_user_playbooks
Retrieve user playbook rows containing agent guidance, including entries extracted from interactions and rows created by supported manual or playbook-optimizer workflows.
response = client.get_user_playbooks(
limit=100,
tags=["support"]
)curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/get_user_playbooks" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"limit": 100,
"tags": ["support"]
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
limit? | int | 100 | Maximum number of results to return |
user_playbook_id? | int | — | Exact user playbook ID filter |
user_id? | str | — | Filter by user ID |
request_id? | str | — | Filter by generating request ID |
query? | str | — | Case-insensitive text filter across visible user playbook fields |
playbook_name? | str | — | Filter by playbook name |
agent_version? | str | — | Filter by agent version |
start_time? | datetime | — | Filter by created-at start time |
end_time? | datetime | — | Filter by created-at end time |
status_filter? | list[Status] | — | Filter by status |
tags? | string[] | — | Optional tag filter. Matches user playbooks that have any requested tag. |
add_user_playbook
Add user playbook entries directly to storage.
response = client.add_user_playbook(
user_playbooks=[
{
"agent_version": "v2.1.0",
"request_id": "req_123",
"content": "User expressed satisfaction with response"
}
]
)curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/add_user_playbook" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"user_playbooks": [
{
"agent_version": "v2.1.0",
"request_id": "req_123",
"content": "User expressed satisfaction with response"
}
]
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
user_playbooks | list[UserPlaybook | dict] | — | List of user playbook entries. Each item is a `UserPlaybook` model or a plain dict with the same fields — click through to see the schema. |
At least one of content or trigger must be provided.
add_agent_playbooks
Add agent playbook entries directly to storage.
response = client.add_agent_playbooks(
agent_playbooks=[
{
"agent_version": "v2.1.0",
"content": "Agent should provide more concise responses",
"playbook_status": "approved",
"playbook_metadata": "{}"
}
]
)curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/add_agent_playbook" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"agent_playbooks": [
{
"agent_version": "v2.1.0",
"content": "Agent should provide more concise responses",
"playbook_status": "approved",
"playbook_metadata": "{}"
}
]
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
agent_playbooks | list[AgentPlaybook | dict] | — | List of agent playbook entries. Each item is an `AgentPlaybook` model or a plain dict with the same fields — click through to see the schema. |
get_agent_playbooks
Retrieve agent playbook entries.
response = client.get_agent_playbooks(
limit=10,
tags=["tone"]
)curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/get_agent_playbooks" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"limit": 10,
"tags": ["tone"]
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
limit? | int | 100 | Maximum number of results |
agent_playbook_id? | int | — | Exact agent playbook ID filter |
query? | str | — | Case-insensitive text filter across visible agent playbook fields |
playbook_name? | str | — | Filter by playbook name |
agent_version? | str | — | Filter by agent version |
start_time? | datetime | — | Filter by created-at start time |
end_time? | datetime | — | Filter by created-at end time |
status_filter? | list[Status] | — | Filter by status |
tags? | string[] | — | Optional tag filter. Matches agent playbooks that have any requested tag. |
playbook_status_filter? | PlaybookStatus | — | Filter by playbook status. When unset, the server defaults to returning APPROVED playbooks. |
force_refresh? | bool | false | Bypass cache |
Update Methods
update_user_playbook
Update editable fields of a user playbook in place. Pass only the fields you want to change.
response = client.update_user_playbook(
user_playbook_id=42,
content="Refined playbook content",
)curl -X PUT "${REFLEXIO_URL:-https://www.reflexio.ai}/api/update_user_playbook" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"user_playbook_id": 42,
"content": "Refined playbook content"
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
user_playbook_id | int | — | The ID of the user playbook to update. |
playbook_name? | str | — | New playbook category name. |
content? | str | — | New content text. |
trigger? | str | — | New trigger text. |
rationale? | str | — | New rationale text. |
Returns: UpdateUserPlaybookResponse with success and message.
update_agent_playbook
Update editable fields of an agent playbook in place. Pass only the fields you want to change.
response = client.update_agent_playbook(
agent_playbook_id=17,
content="Updated guidance"
)curl -X PUT "${REFLEXIO_URL:-https://www.reflexio.ai}/api/update_agent_playbook" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"agent_playbook_id": 17,
"content": "Updated guidance"
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
agent_playbook_id | int | — | The ID of the agent playbook to update. |
playbook_name? | str | — | New playbook category name. |
content? | str | — | New content text. |
trigger? | str | — | New trigger text. |
rationale? | str | — | New rationale text. |
playbook_status? | PlaybookStatus | — | New approval status. To change ONLY the status, prefer update_agent_playbook_status — it has tighter semantics and a single-purpose endpoint. |
Returns: UpdateAgentPlaybookResponse with success and message.
update_agent_playbook_status
Dedicated endpoint for the approval workflow (approve / pending / reject). Use this instead of update_agent_playbook when the only change is the playbook_status — the server enforces tighter validation and writes a smaller change log.
from reflexio import PlaybookStatus
response = client.update_agent_playbook_status(
agent_playbook_id=17,
playbook_status=PlaybookStatus.APPROVED,
)curl -X PUT "${REFLEXIO_URL:-https://www.reflexio.ai}/api/update_agent_playbook_status" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"agent_playbook_id": 17,
"playbook_status": "approved"
}
JSON| Prop | Type | Default | Description |
|---|---|---|---|
agent_playbook_id | int | — | The ID of the agent playbook. |
playbook_status | PlaybookStatus | — | New approval status: PENDING, APPROVED, or REJECTED. |
Returns: UpdatePlaybookStatusResponse with success and message.
delete_agent_playbook
Delete an agent playbook by ID.
response = client.delete_agent_playbook(
agent_playbook_id=123,
wait_for_response=True
)curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_agent_playbook" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"agent_playbook_id": 123
}
JSONdelete_user_playbook
Delete a user playbook by ID.
response = client.delete_user_playbook(
user_playbook_id=456,
wait_for_response=True
)curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_user_playbook" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"user_playbook_id": 456
}
JSONBulk Delete Operations
delete_agent_playbooks_by_ids
Delete multiple agent playbooks by their IDs.
response = client.delete_agent_playbooks_by_ids(agent_playbook_ids=[1, 2, 3])curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_agent_playbooks_by_ids" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"agent_playbook_ids": [
1,
2,
3
]
}
JSONReturns: BulkDeleteResponse with success, deleted_count, and message
delete_user_playbooks_by_ids
Delete multiple user playbooks by their IDs.
response = client.delete_user_playbooks_by_ids(user_playbook_ids=[1, 2, 3])curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_user_playbooks_by_ids" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"user_playbook_ids": [
1,
2,
3
]
}
JSONReturns: BulkDeleteResponse with success, deleted_count, and message
delete_all_playbooks
Delete all playbooks (both user and agent). Cascading variant — wipes both stores.
response = client.delete_all_playbooks()curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_all_playbooks" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY"Returns: BulkDeleteResponse with success, deleted_count, and message
delete_all_user_playbooks
Delete all user playbooks (user only, not agent).
response = client.delete_all_user_playbooks()curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_all_user_playbooks" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY"Returns: BulkDeleteResponse with success, deleted_count, and message
delete_all_agent_playbooks
Delete all agent playbooks (agent only, not user).
response = client.delete_all_agent_playbooks()curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_all_agent_playbooks" \
-H "User-Agent: my-agent-reflexio" \
-H "Authorization: Bearer $REFLEXIO_API_KEY"Returns: BulkDeleteResponse with success, deleted_count, and message