Goal
Move posts from Inbox → Approved, optionally → Scheduled, based on workspace governance and scheduling preferences.
Which Agent Types Use This
- reviewer — Primary use case (human-guided approvals or auto-approvals after triage)
- custom — User-defined agents with approval permissions
Hard Rules
- Must have Editor or Admin role (only these roles can approve).
- Scheduling requires additional permission (
posts:schedule capability).
- Never publish directly — this skill routes to
approved or scheduled, NOT published.
- Respect per-post metadata:
- If post has
suggested_time in metadata, use it (unless overridden)
- If post has
do_not_schedule flag, skip scheduling even if requested
- If Safe Mode ON: scheduling is separate action after approval.
- If Safe Mode OFF: approval can include scheduling in one step.
Steps
1. Check policy & capabilities
bolta.get_workspace_policy(workspace_id) → extract safe_mode
bolta.get_my_capabilities(workspace_id) → verify review:approve and posts:schedule
- If missing
review:approve: fail immediately (unauthorized)
2. Load post metadata
- For each
post_id:
bolta.get_inbox_item(post_id) → fetch post content, current state, suggested_time, metadata
- Verify state is
inbox (can't approve what's not in inbox)
- Extract
suggested_time from metadata if present
3. Approve posts (Inbox → Approved)
- For each post_id:
bolta.approve_post(post_id)
- On success: post transitions to
approved state
- Add to
approved_ids
- If agent_id provided: add comment
bolta.add_comment(post_id, "Approved by {agent.name}")
- On failure (permission denied, invalid state):
- Add to
failed array with reason
- Continue with remaining posts
4. Route to Scheduled (if requested)
If schedule_mode == "approve_only":
- Stop here. Posts remain in
approved state.
- Human can schedule them later manually.
final_states[post_id] = "approved"
If schedule_mode == "use_suggested_time":
- For each approved post:
- If post has
suggested_time in metadata:
- Validate time (must be future, > now + 5 minutes)
bolta.schedule_post(post_id, suggested_time)
- On success: add to
scheduled_ids, final_states[post_id] = "scheduled"
- On failure: add to
failed, post remains approved
- Else (no suggested_time):
- Add warning: "No suggested_time, post remains approved"
final_states[post_id] = "approved"
If schedule_mode == "set_fixed_time":
- Requires
fixed_time parameter
- For each approved post:
bolta.schedule_post(post_id, fixed_time)
- On success: add to
scheduled_ids
- On failure: add to
failed, post remains approved
If schedule_mode == "use_agent_memory":
- Requires
agent_id
bolta.recall(agent_id, "best_posting_times") → get learned optimal times
- For each approved post (index i):
- Pick schedule time from agent memory
bolta.schedule_post(post_id, calculated_time)
- On success: add to
scheduled_ids
- On failure: add to
failed, post remains approved
If schedule_times array provided:
- Must match length of
post_ids
- For each post (index i):
bolta.schedule_post(post_ids[i], schedule_times[i])
- On success: add to
scheduled_ids
- On failure: add to
failed, post remains approved
5. Handle scheduling failures gracefully
- If platform API fails (Twitter down, account disconnected):
- Post remains in
approved state
- Add to
failed with specific error
- Don't revert approval (human can retry scheduling later)
- If schedule time invalid (past, too soon):
- Add to
failed with validation error
- Post remains
approved
6. Update agent memory (if agent context)
- If
agent_id provided:
bolta.remember(agent_id, "last_approval_batch_size", post_ids.length)
bolta.remember(agent_id, "approval_success_rate", success_count / total_count)
- If scheduling used:
bolta.remember(agent_id, "scheduled_times_used", schedule_times)
Output
{
"approved_ids": ["uuid1", "uuid2", "uuid3"],
"scheduled_ids": ["uuid1", "uuid2"],
"failed": [
{
"post_id": "uuid3",
"reason": "Platform API error: Twitter rate limit exceeded",
"recoverable": true
}
],
"final_states": {
"uuid1": "scheduled",
"uuid2": "scheduled",
"uuid3": "approved"
}
}
Failure Handling
- Partial approval failure: If some posts fail to approve, continue with others.
- Partial scheduling failure: If some fail to schedule, others still succeed.
- Never revert approvals: Once approved, post stays approved even if scheduling fails.
- Bulk operation resilience: One failure doesn't block the batch.
V2 Agent Context
When agents use this skill:
Reviewer agent reasoning:
"I triaged 8 inbox items and identified 5 as ready to approve. I'll approve them with schedule_mode=use_agent_memory to use the posting times I've learned perform best."
Key workflow:
bolta.inbox.triage → identify ready items
bolta.review.approve_and_route → approve + schedule
bolta.remember → store approval patterns
Example Use Cases
Scenario 1: Human Manual Approval (No Scheduling)
{
"workspace_id": "uuid",
"post_ids": ["uuid1", "uuid2", "uuid3"],
"schedule_mode": "approve_only"
}
Result: 3 posts → approved state, human schedules them later
Scenario 2: Bulk Approve & Schedule with Suggested Times
{
"workspace_id": "uuid",
"post_ids": ["uuid1", "uuid2", "uuid3"],
"schedule_mode": "use_suggested_time"
}
Result: 3 posts approved → scheduled using each post's suggested_time metadata
Scenario 3: Reviewer Agent Auto-Approval
- Reviewer agent runs
bolta.inbox.triage
- Identifies 8 posts as "ready to approve" (high confidence)
- Calls this skill with those 8 post_ids +
schedule_mode: use_agent_memory
- Agent uses learned optimal posting times
- 8 posts → approved → scheduled
- Human reviews retrospectively
Scenario 4: Agency Bulk Scheduling
- Agency approves 20 client posts
- Wants them all to go out at 10am tomorrow
schedule_mode: set_fixed_time, fixed_time: "2026-02-21T10:00:00Z"
- 20 posts → approved → scheduled for same time
Scenario 5: Partial Failure Recovery
- Approve 5 posts, schedule 5
- 2 scheduling calls fail (Twitter API down)
- Output: 5 approved, 3 scheduled, 2 failed (but still approved)
- Human can retry scheduling the 2 later when Twitter is back
1---2name: bolta-review-approve-and-route3version: 2.0.04description: Approve inbox posts and route to Approved→Scheduled based on workspace governance. Handles bulk approvals with flexible scheduling.5category: review6roles_allowed: [Editor, Admin]7required_scopes: [review:approve, workspace:read]8tools_required:9 - bolta.get_workspace_policy10 - bolta.get_my_capabilities11 - bolta.get_inbox_item12 - bolta.approve_post13 - bolta.schedule_post14 - bolta.update_post15 - bolta.add_comment16inputs_schema:17 type: object18 required: [workspace_id, post_ids]19 properties:20 workspace_id: { type: string }21 agent_id: { type: string, description: "Agent executing approval (optional, V2, for audit)" }22 post_ids: { type: array, items: { type: string }, description: "Posts to approve (can be bulk)" }23 schedule_mode: { type: string, enum: [approve_only, use_suggested_time, set_fixed_time, use_agent_memory], default: "approve_only" }24 fixed_time: { type: string, description: "ISO timestamp if schedule_mode is set_fixed_time" }25 schedule_times: { type: array, items: { type: string }, description: "Per-post schedule times (length must match post_ids)" }26outputs_schema:27 type: object28 properties:29 approved_ids: { type: array, items: { type: string } }30 scheduled_ids: { type: array, items: { type: string } }31 failed: { type: array, items: { type: object } }32 final_states: { type: object, description: "post_id → final_state map" }33organization: bolta.ai34author: Max Fritzhand35---3637## Goal38Move posts from Inbox → Approved, optionally → Scheduled, based on workspace governance and scheduling preferences.3940## Which Agent Types Use This41- **reviewer** — Primary use case (human-guided approvals or auto-approvals after triage)42- **custom** — User-defined agents with approval permissions4344## Hard Rules451. Must have Editor or Admin role (only these roles can approve).462. Scheduling requires additional permission (`posts:schedule` capability).473. Never publish directly — this skill routes to `approved` or `scheduled`, NOT `published`.484. Respect per-post metadata:49 - If post has `suggested_time` in metadata, use it (unless overridden)50 - If post has `do_not_schedule` flag, skip scheduling even if requested515. If Safe Mode ON: scheduling is separate action after approval.526. If Safe Mode OFF: approval can include scheduling in one step.5354## Steps5556### 1. Check policy & capabilities57- `bolta.get_workspace_policy(workspace_id)` → extract `safe_mode`58- `bolta.get_my_capabilities(workspace_id)` → verify `review:approve` and `posts:schedule`59- If missing `review:approve`: fail immediately (unauthorized)6061### 2. Load post metadata62- For each `post_id`:63 - `bolta.get_inbox_item(post_id)` → fetch post content, current state, suggested_time, metadata64 - Verify state is `inbox` (can't approve what's not in inbox)65 - Extract `suggested_time` from metadata if present6667### 3. Approve posts (Inbox → Approved)68- For each post_id:69 - `bolta.approve_post(post_id)`70 - On success: post transitions to `approved` state71 - Add to `approved_ids`72 - If agent_id provided: add comment `bolta.add_comment(post_id, "Approved by {agent.name}")`73 - On failure (permission denied, invalid state):74 - Add to `failed` array with reason75 - Continue with remaining posts7677### 4. Route to Scheduled (if requested)7879**If schedule_mode == "approve_only":**80- Stop here. Posts remain in `approved` state.81- Human can schedule them later manually.82- `final_states[post_id] = "approved"`8384**If schedule_mode == "use_suggested_time":**85- For each approved post:86 - If post has `suggested_time` in metadata:87 - Validate time (must be future, > now + 5 minutes)88 - `bolta.schedule_post(post_id, suggested_time)`89 - On success: add to `scheduled_ids`, `final_states[post_id] = "scheduled"`90 - On failure: add to `failed`, post remains `approved`91 - Else (no suggested_time):92 - Add warning: "No suggested_time, post remains approved"93 - `final_states[post_id] = "approved"`9495**If schedule_mode == "set_fixed_time":**96- Requires `fixed_time` parameter97- For each approved post:98 - `bolta.schedule_post(post_id, fixed_time)`99 - On success: add to `scheduled_ids`100 - On failure: add to `failed`, post remains `approved`101102**If schedule_mode == "use_agent_memory":**103- Requires `agent_id`104- `bolta.recall(agent_id, "best_posting_times")` → get learned optimal times105- For each approved post (index i):106 - Pick schedule time from agent memory107 - `bolta.schedule_post(post_id, calculated_time)`108 - On success: add to `scheduled_ids`109 - On failure: add to `failed`, post remains `approved`110111**If schedule_times array provided:**112- Must match length of `post_ids`113- For each post (index i):114 - `bolta.schedule_post(post_ids[i], schedule_times[i])`115 - On success: add to `scheduled_ids`116 - On failure: add to `failed`, post remains `approved`117118### 5. Handle scheduling failures gracefully119- If platform API fails (Twitter down, account disconnected):120 - Post remains in `approved` state121 - Add to `failed` with specific error122 - Don't revert approval (human can retry scheduling later)123- If schedule time invalid (past, too soon):124 - Add to `failed` with validation error125 - Post remains `approved`126127### 6. Update agent memory (if agent context)128- If `agent_id` provided:129 - `bolta.remember(agent_id, "last_approval_batch_size", post_ids.length)`130 - `bolta.remember(agent_id, "approval_success_rate", success_count / total_count)`131 - If scheduling used: `bolta.remember(agent_id, "scheduled_times_used", schedule_times)`132133## Output134```json135{136 "approved_ids": ["uuid1", "uuid2", "uuid3"],137 "scheduled_ids": ["uuid1", "uuid2"],138 "failed": [139 {140 "post_id": "uuid3",141 "reason": "Platform API error: Twitter rate limit exceeded",142 "recoverable": true143 }144 ],145 "final_states": {146 "uuid1": "scheduled",147 "uuid2": "scheduled",148 "uuid3": "approved"149 }150}151```152153## Failure Handling154- **Partial approval failure:** If some posts fail to approve, continue with others.155- **Partial scheduling failure:** If some fail to schedule, others still succeed.156- **Never revert approvals:** Once approved, post stays approved even if scheduling fails.157- **Bulk operation resilience:** One failure doesn't block the batch.158159## V2 Agent Context160161**When agents use this skill:**162163**Reviewer agent reasoning:**164> "I triaged 8 inbox items and identified 5 as ready to approve. I'll approve them with schedule_mode=use_agent_memory to use the posting times I've learned perform best."165166**Key workflow:**1671. `bolta.inbox.triage` → identify ready items1682. `bolta.review.approve_and_route` → approve + schedule1693. `bolta.remember` → store approval patterns170171## Example Use Cases172173### Scenario 1: Human Manual Approval (No Scheduling)174```json175{176 "workspace_id": "uuid",177 "post_ids": ["uuid1", "uuid2", "uuid3"],178 "schedule_mode": "approve_only"179}180```181**Result:** 3 posts → `approved` state, human schedules them later182183### Scenario 2: Bulk Approve & Schedule with Suggested Times184```json185{186 "workspace_id": "uuid",187 "post_ids": ["uuid1", "uuid2", "uuid3"],188 "schedule_mode": "use_suggested_time"189}190```191**Result:** 3 posts approved → scheduled using each post's suggested_time metadata192193### Scenario 3: Reviewer Agent Auto-Approval194- Reviewer agent runs `bolta.inbox.triage`195- Identifies 8 posts as "ready to approve" (high confidence)196- Calls this skill with those 8 post_ids + `schedule_mode: use_agent_memory`197- Agent uses learned optimal posting times198- 8 posts → approved → scheduled199- Human reviews retrospectively200201### Scenario 4: Agency Bulk Scheduling202- Agency approves 20 client posts203- Wants them all to go out at 10am tomorrow204- `schedule_mode: set_fixed_time`, `fixed_time: "2026-02-21T10:00:00Z"`205- 20 posts → approved → scheduled for same time206207### Scenario 5: Partial Failure Recovery208- Approve 5 posts, schedule 5209- 2 scheduling calls fail (Twitter API down)210- Output: 5 approved, 3 scheduled, 2 failed (but still approved)211- Human can retry scheduling the 2 later when Twitter is back