Skip to content

Opportunity Approvals

Opportunity Approvals gives an enabled Bombyx organization a shared review inbox. A connector can submit discoveries and suggested drafts. An authenticated organization member can edit a draft and authorize one action. A connector with execution access can then claim, revalidate, perform, and record that action.

Agents cannot approve their own work. Approval exists only in the authenticated Bombyx app.

The app opens on Review, which contains only complete, currently approvable targets. Agent-owned research, claimed work, scheduled retries, and recovery that needs attention appear under In progress. Final publication outcomes and closed items appear under History, so ordinary agent work does not look blocked. Connectors should use draft_ready only after the source, supported action, and suggested draft are ready for a member decision.

Use separate connector capabilities:

  • operator_opportunities_write submits discoveries and draft revisions.
  • operator_authorizations_execute reads and executes member-approved actions.
  • operator_learning_review reads approved revision evidence and applies a reviewed learning batch.
  • memory_propose is additionally required when applying a learning batch, because factual corrections become reviewable Brain proposals.

The feature must also be enabled for the organization. A valid capability does not bypass a disabled organization feature.

Send POST /app/api/tool-connectors/v1/operator/opportunities with a stable source_system and external_key. Reusing both values updates the existing organization record instead of creating a duplicate.

Use record_type: target only for a concrete public target. Use scan_outcome for useful channel-level results such as no qualified finding or an identity problem. Scan outcomes cannot be authorized directly.

{
"source_system": "scheduled_operator",
"external_key": "facebook-post-123",
"run_key": "round-2026-07-12-am",
"record_type": "target",
"platform": "facebook",
"community": "Example page",
"canonical_url": "https://www.facebook.com/example/posts/123/",
"target_key": "target:facebook:bf8f620453a8e6b8b1cc377f8e0b3dc0",
"native_target_ids": {"post_id": "123"},
"score": 0.82,
"recommendation": "act",
"action_type": "comment",
"status": "draft_ready",
"title": "Owner asks about a paint chip",
"summary": "A concise description of the opportunity.",
"rationale": "Why participation would be useful and appropriate.",
"blockers": [],
"next_action": null,
"next_action_due_at": null,
"metadata": {
"review_context": {
"version": 1,
"visibility": "public",
"kind": "reply_target",
"author": "Original poster",
"handle": "u/example",
"text": "The public comment or post text the draft will answer.",
"parent_title": "Source thread, video, question, or post title",
"captured_at": "2026-07-12T18:00:00Z",
"content_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
},
"draft": {
"content": "Suggested response text.",
"disclosure": "Organization representative."
}
}

Send target_key and native_target_ids at the top level. The target key is the stable public-object identity shared with the executing operator. Native platform IDs take priority over URL text. Bombyx computes the same key from the normalized URL as a compatibility fallback when an older connector omits it, but new connectors should send both fields explicitly.

For non-Reddit targets, the backward-compatible v1 context remains supported: send metadata.review_context.version: 1, visibility: public, kind: reply_target or comment_target, and nonblank original public text. Include parent_title whenever available. Optional author, handle, captured_at, and content_hash help reviewers and are preserved in the approval snapshot. Missing or private context is not allowed in Review; legacy rows without it stay in In progress as Context needed before review.

New Reddit reply and comment targets require review context v2. The context is public-only and thread-first, with the exact target permalink, source post, bounded related public comments, and a server-derived aggregate context_hash. The top-level platform must match review_context.platform, and top-level canonical_url must identify the same exact target as target_comment.permalink; a mismatch remains agent-owned and cannot be approved. Reddit items can enter Review only when value_add is distinct_contribution; duplicates, context gaps, and no_worthwhile_response remain agent-owned in In progress.

{
"review_context": {
"version": 2,
"visibility": "public",
"conversation_id": "reddit-thread-123",
"thread_id": "t3_123",
"platform": "reddit",
"community": "r/example",
"community_url": "https://www.reddit.com/r/example/",
"source_post": {
"id": "t3_123",
"title": "How do I repair this paint chip?",
"author": "u/example",
"body": "The public post body.",
"permalink": "https://www.reddit.com/r/example/comments/123/question/",
"created_at": "2026-07-12T18:00:00Z"
},
"media": [{"url": "https://cdn.example.test/photo.jpg", "alt_text": "Paint chip close-up"}],
"target_comment": {
"id": "t1_456",
"parent_id": "t3_123",
"author": "u/example",
"handle": "u/example",
"text": "What should I use before touching this up?",
"permalink": "https://www.reddit.com/r/example/comments/123/question/456/",
"created_at": "2026-07-12T18:05:00Z",
"is_op": true
},
"related_comments": [
{
"id": "t1_455",
"parent_id": "t3_123",
"relationship": "direct_reply",
"author": "u/helpful-detailer",
"handle": "u/helpful-detailer",
"text": "Clean the chip before applying any product.",
"permalink": "https://www.reddit.com/r/example/comments/123/question/455/",
"created_at": "2026-07-12T18:04:00Z",
"is_op": false
}
],
"captured_at": "2026-07-12T18:10:00Z",
"value_add": "distinct_contribution",
"what_this_adds": "A concise preparation step not covered by the thread."
}
}

v2 requires at least one of conversation_id or thread_id, plus source-post ID, title, author, permalink, and timestamp. Source body and community URL are optional when the public source does not provide them. The target and every related comment require an author or handle, exact permalink, timestamp, and is_op; related comments also use one of parent_path, direct_reply, or relevant. v2 accepts HTTPS web URLs only, limits media to 8 items and related comments to 25, and normalizes text before hashing. The returned and approved snapshot contains the canonical context_hash; clients must not supply or rely on a client-computed hash. Approval freezes the complete normalized context, semantic value-add decision, and proposed public identity.

Use blockers only for actual hard gates. Put cautions and risk notes in metadata, and use next_action, next_action_due_at, and lifecycle_reason_code for agent-owned pending work.

At the start of each run:

  1. List approvals with GET /app/api/tool-connectors/v1/operator/authorizations.
  2. Choose an item with authorization.claimable: true, then claim it with POST .../{id}/claim and a unique run key for that execution attempt. claimable is only a read-only lifecycle eligibility candidate: it is true for an active approval or a retry whose scheduled time has arrived. It is false after a claim and for completed, expired, revoked, or not-yet-due work. It does not reserve the action. POST /claim remains authoritative and can still reject because another live claim exists, the executor state changed, a concurrent update won the race, or the operator’s downstream local-capacity gate is unavailable. Keep the returned attempt_id. Repeating a still-active claim with the same run key is idempotent. One executor can hold only one live claimed authorization at a time across the organization, so finish or release it before claiming the next approval.
  3. Re-read the live target and apply the organization and platform rules.
  4. Call GET .../{id}?run_key={run_key}&attempt_id={attempt_id} immediately before submission. This confirms the approving member still belongs to the organization, the authorization remains unrevoked, and the exact 15-minute claim remains active.
  5. Perform one action and verify the visible result.
  6. Record completion with the same run_key and attempt_id, final content, the public URL, visible identity, and verification data.

Every revalidate, completion, block, verification-required, and release request is bound to the exact run and attempt returned by claim. An expired lease is never auto-reclaimed because the interrupted run may have submitted before it stopped. Reconcile that exact attempt by releasing it with proof that no public mutation occurred, or use verification-required when the result is uncertain.

Ordinary GET preflight calls are idempotent and do not consume recovery. claimable chooses pre-claim work. execution_allowed is separate and becomes true only for the exact live claimed attempt. Require authorization.execution_allowed: true before any public mutation. A stale lease, expired authorization, or removed approver returns the exact claimed attempt read-only with reconciliation_required: true and a reason; the GET never destroys the release or quarantine path. If the agent corrects one recoverable problem inside the same attempt, call the same GET once with recovery=true. A second explicit recovery is rejected, but another ordinary immediate-before-submit preflight remains available. If the connector loses the completion response, call the same exact-run and attempt GET again. A completed authorization returns its immutable nested authorization.attempt, including final content, public URL, visible identity, verification, and performed_at, without performing another public action.

Treat the authorization’s top-level decision_key, platform, target_url, target_key, native_target_ids, action_type, and provenance_snapshot as the immutable execution contract. Bombyx captures them when a member approves the action. The nested opportunity remains current review context and can change on a later sync, so it must not replace the authorization snapshot during execution or ledger reconciliation.

provenance_snapshot also contains the target key and native target IDs recorded at approval. A released attempt is immutable. Schedule a future retry and claim it with a new run key, which creates a new attempt while preserving the prior evidence.

The authorization uses open_discretion. The draft is guidance, not an exact-text requirement. The connector may adapt wording, but organization knowledge, identity, platform rules, and other hard gates remain mandatory.

  • Use /block only for one of the documented hard-gate codes: captcha_or_security_challenge, account_restricted, identity_mismatch, phone_verification_required, password_reset_required, passkey_or_security_key_required, identity_document_required, paid_access_required, nonroutine_terms_required, or policy_prohibits_action.
  • Use /verification-required when submission may have occurred but the result cannot be confirmed. Never retry that action blindly.
  • Use /release for recoverable work only after checking the public target and composer. Send a supported reason_code, a future next_retry_at, and structured proof_no_mutation with confirmed: true. The retry remains approved and appears as agent work. After 24 hours of recoverable attempts, automatic retries stop in needs_attention without mislabeling the item as blocked. Omit next_retry_at only when reconciling an authorization that already expired, was revoked, or whose approver left the organization; those releases preserve expired or revoked and cannot retry.
{
"run_key": "round-2026-07-14-am-youtube-1",
"attempt_id": 42,
"reason_code": "composer_unavailable",
"reason": "The visible reply composer did not load.",
"next_retry_at": "2026-07-14T23:00:00Z",
"proof_no_mutation": {
"confirmed": true,
"checked_at": "2026-07-14T22:42:00Z",
"method": "Public thread and composer reread"
},
"reconciliation": {"public_result": "absent"}
}

An authorization expires seven days after approval and permits one successful public action.

Saving a revision does not train the Assistant. A revision becomes eligible learning evidence only after an authenticated organization member approves that exact user-authored revision.

Use GET /app/api/tool-connectors/v1/operator/learning/evidence to list unprocessed evidence. Each item includes the original tool draft, the saved revision chain, the exact approved revision, and a fingerprint. Intermediate saves are context only. They are not positive examples.

Use GET /app/api/tool-connectors/v1/operator/learning/context to retrieve currently active coaching for the connector’s Assistant.

After a governed analysis pass, submit one idempotent batch to POST /app/api/tool-connectors/v1/operator/learning/apply. The batch must disposition every evidence item and may contain:

  • reusable writing guidance, stored as active Assistant Coaching;
  • possible factual corrections, stored only as pending Brain proposals for member review.

The apply endpoint rechecks that every item still refers to an approved user revision, rejects stale fingerprints, and prevents a reviewed revision from being learned twice. Reusing a batch_key with the same payload is safe. Reusing it with different content is rejected.

Broad coaching requires corroboration. Global guidance must cite at least three independently approved revisions, while answer-mode or topic guidance must cite at least two. A single approved revision can create only narrowly matched question-pattern or single-example coaching with an explicit question or trigger.