---
name: rolino
description: Use Rolino to inspect storage, safely clean unused Media Library files, read authorized SEO opportunities and weekly reports, inspect Blog publishing destinations and delivery status, upload local social media, turn folders of content into drafts, check publishing readiness, schedule or publish approved posts, inspect the social calendar, and configure Rolino CLI or MCP access. Trigger when the user asks an agent to use Rolino, inspect or free Rolino storage, read Rolino SEO guidance, inspect or publish Blog content, schedule content from files or a folder, prepare or publish social content through Rolino, inspect Rolino projects or posts, or connect Rolino to an AI coding agent.
---

# Rolino Agent Workflow

Rolino is an agent-ready social scheduling service. Use its CLI in agent mode for structured output. Keep the user's control over account creation, authorization, connected social accounts, scheduling, and publishing.

## When to use this Rolino skill

Use this skill when the user asks an agent to work in an authorized Rolino workspace. Good fits include inspecting Rolino projects, posts, calendars, publishing connections, Blog plans, Blog deliveries, or authorized SEO tasks; uploading user-approved media; preparing or updating drafts; and previewing an exact schedule, approval, or publication for the user to confirm.

Use Rolino only after the user asks for Rolino or the requested work clearly belongs in their Rolino workspace. Do not use this skill for general web browsing, unrelated analytics, an account the user has not connected, or content that the user does not want stored in Rolino. A request to prepare content is a draft request unless the user separately asks to schedule or publish it.

Use the hosted MCP endpoint for a remote MCP client, the CLI or local stdio MCP server for work on the user's computer, and the SDK or Agent API for an existing integration. Always resolve the exact workspace and project before a write.

## Command prefix

Prefer an installed CLI:

~~~bash
rolino --agent <command>
~~~

If it is not installed, use the current public package without modifying the project:

~~~bash
npx -y @rolino/cli@latest --agent <command>
~~~

For self-hosted Rolino, preserve the user's configured ROLINO_URL or pass --base-url. Never guess a deployment URL or copy a credential into source code, logs, prompts, or a final response.

## Start with prerequisites

1. Check authentication with `rolino --agent auth status`.
2. If authentication is missing, explain that the user needs a Rolino account and browser approval. Run `rolino auth login` only when the user wants to continue, then wait for them to sign up or sign in and approve the requested workspace permissions in the browser.
3. After approval, rerun `rolino --agent auth status`, then `rolino --agent projects list`.
4. Resolve the project by an exact ID, a clearly matching name, or its website. Never silently choose between plausible projects.
5. If no project exists, ask before creating one. Use `rolino --agent projects create --name "NAME" --type PRODUCT --yes`; add a real website only when known. Project creation does not connect accounts, create posts, schedule, or publish.
6. Before preparing a destination, run `rolino --agent integrations health --project PROJECT_ID`. If the required social account is disconnected, tell the user to connect it at `/projects/PROJECT_ID/settings/connections`, then rerun the health check.
7. For TikTok, refresh creator-specific choices with `rolino --agent integrations delivery-options --project PROJECT_ID --provider TIKTOK --account INTEGRATION_ID` immediately before preparing settings. Use only returned modes, visibility values, interaction availability, and duration limits.
8. For LinkedIn, refresh `rolino --agent integrations delivery-options --project PROJECT_ID --provider LINKEDIN --account INTEGRATION_ID` before preparing a draft. Confirm that the connected member is the intended author and use only the returned text/image/video policy. Native video requires exactly one MP4 and cannot be mixed with images. LinkedIn Company Pages are not member-publishing targets.
9. For Google Business Profile, confirm the selected location name in readiness. Use text and at most one stored JPEG or PNG. Rolino owns schedule time. If the result is unknown, inspect the selected Business Profile and do not retry the create request.

## Select the exact publishing account

Use the integrationId returned by integrations health for the intended connected account. A provider name alone does not identify an account when a brand has multiple connections. Pass --account INTEGRATION_ID to integrations delivery-options, or integrationId to MCP refresh_delivery_options and the SDK deliveryOptions request options. Check that the returned integrationId and accountId match the selected connection before preparing a draft. Stop on ACCOUNT_SELECTION_REQUIRED or a different identity; do not fall back to another account.

For exact-account drafts, pass destinations with provider, integrationId, and account-specific settings through MCP or SDK. CLI accepts --accounts-file with the same destination array. Preserve that identity through readiness, preview, and execution. TikTok visibility must come from the refreshed account choices. If Everyone is unavailable, ask the user to choose an available audience; do not replace public publishing with a private post or inbox draft without their instruction.

The abbreviated --platform examples below apply only when a provider has one unambiguous eligible account. Otherwise, use --accounts-file for draft creation and the saved --destination ID for publish preview and execute. Use destinationIds in MCP or SDK publishing input. Read these IDs from the saved post; never substitute the provider name for an exact destination.

Hosted MCP updates take effect when the application is deployed. Local stdio MCP and CLI installations receive package fixes through an npm update; update the installed entry-point package and restart the local MCP process. Do not change a hosted connection to local MCP to work around a provider or account error.

## Facebook Page video workflow

Refresh integrations delivery-options with --provider FACEBOOK --account INTEGRATION_ID for the exact Page. Check video.available before preparing video settings. The owner must connect or reconnect through the returned authorization.path in the browser.

Set facebookSettings.format to FEED for one MP4 or MOV in its original aspect ratio, including square, 4:5, landscape, or vertical. Set it to REEL for one vertical 9:16 MP4, at least 540 by 960 pixels, 3 to 90 seconds, and 24 to 60 fps. Use stored media, at most 1 GB. Both video formats are public. Set linkUrl to null and put links in the caption. Do not mix images and video or attach several videos.

Use the exact integrationId in destinations or a CLI --accounts-file. Use the saved destination ID for publication and recovery. Preview and confirm the selected Page and time through the shared workflow. Scheduled uploads start when due; Facebook processing can delay publication. A queued or processing result is not proof of publication.

After an uncertain result, use check_post_delivery or the CLI delivery check for the saved destination. Preserve the remote video ID and inspect the original Page. Never create a replacement upload or change the format to bypass an unresolved result, even if no ID was saved.

The Page video release requires matching updated SDK, CLI, and local MCP packages. Older packages can reject VIDEO in delivery options. Update the installed entry-point package after the release is published, then restart local MCP. Refresh this skill from the configured Rolino host. Hosted MCP receives the change with the application deployment. A response validation error does not authorize publication or a retry.

## Bluesky workflow

Set image descriptions with mediaAltText or CLI --media-alt. Untouched draft deletion requires posts:delete; account disconnection requires integrations:disconnect. Both use separate preview and confirmed execution with a stable idempotency key. Existing grants do not gain these optional permissions. Report unresolved provider revocation and direct the owner to remove Rolino access in Bluesky settings.


Run integrations delivery-options with --provider BLUESKY --account INTEGRATION_ID to read account health, limits, and the owner OAuth authorization.path on the current Rolino host. The owner must connect or reconnect in the browser. Use text or up to four JPEG, PNG, or WebP images, each no larger than 2,000,000 bytes, with captions of at most 300 graphemes. Video and external cards are unsupported.

Hosted MCP offers prepare_media_asset_upload and complete_media_asset_upload. Transfer exact approved bytes to the returned private upload URL between calls; completion verifies stored media. The CLI and local MCP can upload caller-local files directly. Use completed asset IDs in drafts.

To cancel a future local schedule, use posts schedule cancel preview and execute, or preview_post_schedule_cancellation and execute_post_schedule_cancellation. Require posts:schedule, the current version, user approval of the exact preview, its token, and a stable idempotency key. CLI execute also requires --yes. All listed destinations return to Draft. Started or uncertain delivery must use check_post_delivery first. To edit scheduled Bluesky content, cancel, update the draft, then preview and confirm a new schedule. A queue result is not proof of publication.

## Interpret common requests

- “Show my SEO opportunities” → confirm that the connection has the optional `seo:read` scope, resolve the exact project, then run `rolino --agent seo opportunities list --project PROJECT_ID`. Read detail only by an ID returned for that project.
- “Show my weekly SEO report” → confirm `seo:read`, resolve the project, list reports, then read the selected report by its exact ID. SEO commands are read-only. They cannot run analysis, change settings, delete data, enable reports, send email, or call an external provider.
- “Find backlink opportunities” → confirm `backlinks:write`, resolve the exact project, list or add owned target pages, then start one bounded discovery with a stable idempotency key. Poll the returned run. Do not supply an arbitrary query or infer a contact.
- “Review backlink prospects” → confirm `backlinks:read`, list bounded prospects, and read detail only by an ID returned for the same project. This scope omits email addresses and draft bodies.
- “Prepare backlink outreach” → confirm `backlinks:read`, `backlinks:write`, and `backlinks:contacts`. Research only an approved stored prospect. Accept only a non-stale public contact with its exact source. Review or edit the draft. Rolino never sends email.
- “Show my Blog plan” → confirm `blog:read`, resolve the exact project, then run `rolino --agent blog plans --project PROJECT_ID`. The Blog plan is separate from the social calendar.
- “Change my Blog cadence” → confirm `blog:write`, read the exact plan and version, run `rolino --agent blog plan cadence preview`, show every editorial date move and locked item, then run `rolino --agent blog plan cadence apply` only with the unchanged weekdays, plan version, schedule digest, confirmation token, and a stable idempotency key. This never schedules, publishes, or unpublishes an article.
- “Edit this Blog article” → confirm `blog:write`, read the exact article and version, save the complete draft through `rolino --agent blog articles update ARTICLE_ID --project PROJECT_ID --input DRAFT.json --yes`, then read it again. This never publishes.
- “Approve this Blog article” → confirm `blog:approve`, preview the exact revision and image bundle, inspect its links and warnings, then execute only the unchanged preview with its confirmation token and a stable idempotency key. This cannot publish.
- “Publish this Blog article” → confirm separate `blog:publish` access and exact-bundle approval, run the Blog publish or schedule preview, show its revision, destination, time, timezone, and URL, then execute only the unchanged preview with its confirmation token and a stable idempotency key. A changed or unapproved bundle must stop and restart from approval preview.
- “Show my Blog publishing destinations” → confirm `blog:read`, resolve the exact project, then run `rolino --agent blog destinations list --project PROJECT_ID`. Use `blog destinations deliveries` to report safe delivery state and remote URLs. Do not infer that a queued delivery is live.
- “Set up Blog Studio” → use the agent-first Blog workflow below. Ask for the intended Review required, Agent approval, or Full publishing access level. The user grants each requested capability in Rolino; the prompt does not grant access and an agent cannot widen its credential.

- “Show my Rolino calendar” → resolve the project, then run `rolino --agent calendar list --project PROJECT_ID`.
- “Create/prepare/draft a post” → create a draft only. Do not schedule or publish unless the user separately asks.
- “Schedule this folder” → inspect supported local media without modifying it, build a file-to-caption/destination/time manifest, show ambiguities and blockers, and obtain approval for that exact batch before uploading and scheduling it.
- “Schedule this post” → create or resolve the draft, check readiness, preview the exact schedule, show the user the time, timezone, destinations, and blockers, then execute only after the user has clearly approved that exact result.
- “Publish this now” → create or resolve the draft, check readiness, preview the exact destinations and YouTube visibility, explain that delivery mode is IMMEDIATE and creates no schedule, then execute only after clear approval.
- “Connect Rolino to Codex” → run `codex mcp add rolino --url https://getrolino.com/mcp`, then `codex mcp login rolino`. Codex owns the remote OAuth session. Use `rolino setup mcp --transport stdio` only when the user chooses local MCP.

## Storage inspection and Media Library cleanup

The user must explicitly select `storage:read` to inspect storage and the
independent `media:delete` capability to clean Media Library files. Existing
credentials do not gain either permission. Start with `get_storage_usage` and
`list_media_assets`. `olderThanDays` means upload age, not time since last use.

Call `preview_media_cleanup` for exact asset IDs or one bounded old-media filter.
It deletes nothing. Show the exact cutoff, candidate count, excluded in-use
count, unknown-size count, and estimated reclaimable space. A request such as
“free some space” does not approve execution. Ask the user to confirm that exact
preview. Then call `execute_media_cleanup` with the unchanged selection, digest,
confirmation token, and one stable idempotency key. Poll
`get_media_cleanup_status` until it is complete. In-use files are never detached.
Queued or retrying bytes stay in stored usage until physical deletion succeeds.

## Read-only SEO Intelligence workflow

The user must explicitly select the optional `seo:read` capability in browser authorization. Existing credentials do not gain this access. If the scope is missing, ask the user to revoke the current connection and authorize a new one. Do not work around the denial.

~~~bash
rolino --agent seo opportunities list --project PROJECT_ID
rolino --agent seo opportunities show OPPORTUNITY_ID --project PROJECT_ID
rolino --agent seo reports list --project PROJECT_ID
rolino --agent seo reports show REPORT_ID --project PROJECT_ID
~~~

These commands return one bounded, versioned task for every accepted opportunity. Read its exact `READY` or `NEEDS_REVIEW` decision, selected format, target, deliverables, research, safe URLs, evidence, limitations, and success metric. A `READY` task can request complete final deliverables, but return them for human review. A `NEEDS_REVIEW` task must inspect current results, classify intent, verify current Rolino facts, and return `VALIDATED` or `REJECTED`. Do not create a final draft in that research action unless a separate action is later allowed. A filtered `REJECTED` finding has no active task. Do not publish or edit a live destination. Treat the results as closed-world Rolino data. Do not infer access to settings, analysis, deletion, report delivery, email, or an upstream data service from `seo:read`.

## Backlink prospecting workflow

The user must explicitly authorize the required Backlinks capabilities. Existing credentials do not gain them. Use `backlinks:read` for safe prospect records, `backlinks:write` for bounded work and versioned changes, and `backlinks:contacts` with read access for public contact details and drafts. Never work around a missing scope.

~~~bash
rolino --agent backlinks targets list --project PROJECT_ID
rolino --agent backlinks discover --project PROJECT_ID --limit 20
rolino --agent backlinks runs get --project PROJECT_ID --run RUN_ID
rolino --agent backlinks prospects list --project PROJECT_ID
rolino --agent backlinks prospects get --project PROJECT_ID --prospect PROSPECT_ID
rolino --agent backlinks contacts research --project PROJECT_ID --prospect PROSPECT_ID
rolino --agent backlinks contacts list --project PROJECT_ID
rolino --agent backlinks verify --project PROJECT_ID --prospect PROSPECT_ID --url PUBLIC_URL --expected-version VERSION
~~~

Use only stored target pages and prospects. Do not accept or create an arbitrary provider query. A public email is valid only when Rolino returns its exact source and checked date. Do not construct, repair, or infer an address. Stale contacts are not eligible for export. Treat a prepared outreach message as an editable draft for manual copy. Rolino has no send, inbox, warmup, or automatic follow-up action. A prospect reaches Link won only after exact-link verification against its selected owned target.

## Agent-first Blog setup workflow

Use the CLI, local stdio MCP server, or hosted Streamable HTTP MCP server. Local clients authenticate through `rolino auth login`; remote clients follow the OAuth challenge from `https://getrolino.com/mcp` and approve one Rolino workspace in the browser. The five Blog capabilities are independent. Existing credentials do not gain `blog:approve` or any other new capability. If a required capability is missing, stop and ask the user to authorize it; never work around or widen the scope.

1. Read Blog setup status with `rolino --agent blog setup status --project PROJECT_ID`, then read the current article or plan state.
2. Ask for any missing website, cadence, destination, and access level. Never invent an origin or choose an access level for the user.
3. Import and plan through durable jobs. Start only the required operation, poll its returned job, and retry only a reported failure. Never retry blindly.
4. List plan items and present each title, work type, frozen evidence, source limitation, and selection reason before acceptance unless the user's instruction already authorizes that exact bounded plan operation.
5. To change an active plan cadence, call cadence preview first with `blog:write`, show every proposed editorial date change and locked item, and apply only the unchanged preview values with its confirmation token and a stable idempotency key. This operation never changes publication scheduling.
6. `generate_blog_image` already accepts an optional `editorialBrief` of up to 500 characters for image direction. Do not create a second image-direction operation.
5. Create or update one immutable revision with catalog-backed internal links. Use the returned state and version so concurrent edits cannot be overwritten.
6. Generate one Rolino image or upload one user-approved local JPEG, PNG, or WebP. Review the exact image and verify its alt text.
7. If the credential lacks `blog:approve`, stop and ask the user to approve the exact revision and image bundle in Rolino. Do not work around the scope.
8. With `blog:approve`, preview the exact article and image bundle. Inspect the revision, images, links, warnings, and bundle hash, then execute only the unchanged preview with its confirmation token and a stable idempotency key.
9. If the credential lacks `blog:publish`, stop after approval.
10. With `blog:publish`, preview immediate publication or scheduling. Execute only the unchanged preview with the same revision, destination IDs, time, timezone, confirmation token, and a stable idempotency key. Publication never implies approval.
11. Poll delivery attempts with `rolino --agent blog destinations deliveries --project PROJECT_ID --article ARTICLE_ID` and verify the live remote URL. A queued state is not published.
12. If revision content or any selected image changes, the approval is stale. Start again from approval preview; never reuse the earlier approval or publication preview.

Destination setup stays separate from the article workflow. Inspect the provider catalog with `rolino --agent blog destinations providers --project PROJECT_ID` and saved destinations with `rolino --agent blog destinations list --project PROJECT_ID` before configuration. Preview each connection change and ask for confirmation before execution. Store one-time values only in server-side local environment configuration. For Next.js, inspect the current App Router structure, install `@rolino/nextjs-blog` in the repository's existing style, build the target app, verify an authenticated published-content request, and verify signed revalidation. Do not claim a configured token proves delivery.

The local stdio MCP server provides the same reads as `list_blog_publishing_providers`, `list_blog_publishing_destinations`, and `list_blog_delivery_attempts`. The provider catalog requires `blog:manage`. Destination and delivery reads require `blog:read`. These are closed-world reads and do not contact or change a provider. Draft content, prompts, evidence, secrets, and raw provider responses are not returned.

## Draft workflow

Inspect the project before creating anything:

~~~bash
rolino --agent projects show PROJECT_ID
rolino --agent integrations health --project PROJECT_ID
~~~

Create a draft only after the user has supplied or approved the caption and destinations:

~~~bash
rolino --agent posts create --project PROJECT_ID --caption "TEXT" --platform INSTAGRAM --yes
~~~

Repeat `--platform` and `--media` for additional destinations and existing Rolino media asset IDs. Instagram and video-first destinations can require media. Discover reusable assets with `rolino --agent media list --project PROJECT_ID`. Upload an approved local file with `rolino --agent media upload PATH --project PROJECT_ID --yes`, then use the returned asset ID. Never invent an asset ID.

### Website analytics and Astro

Umami and Datafast are website analytics providers. Their stored evidence feeds content ideas and Blog plans. Do not use social integration health or Social performance to inspect them. Obtain separate website-analytics:read access and run rolino --agent website-analytics read --project PROJECT_ID, or read_website_analytics. Read the actual provider, UTC period, coverage, sync status, and active opportunities. Missing pages are unknown, never zero. These reports do not include search queries, rankings, revenue, conversions, or daily trends. These opportunities are separate from canonical SEO tasks; return content suggestions for review and never infer permission to publish.

With website-analytics:manage and an owner or admin role, use discover_website_analytics to discover websites. Choose the specific preview tool: preview_connect_website_analytics, preview_sync_website_analytics, preview_disconnect_website_analytics, or preview_delete_website_analytics_data. Show its resource and consequence, then call the matching execute_ tool only after user approval with unchanged fields, confirmation token, and a stable idempotency key. These tools have no action selector or change envelope. Connection also supports key replacement. Only one website analytics provider can retain a connection. Delete imported data is permanent in Rolino and does not delete provider data. CLI equivalents are website-analytics discover, preview, and execute with --input pointing to a private JSON file. Execute requires --yes in agent mode. Never place API keys in shell arguments, reports, or commits.

For Astro, first list Blog providers and destinations. When enabled, use preview_create_astro_destination and execute_create_astro_destination with blog:manage to create one destination. Save the one-time deliveryToken in the target app's private build/server environment; retries never reveal it again. Install @rolino/astro at the supported version, preserve the saved blog path, and deploy. Use preview_connect_astro_destination and execute_connect_astro_destination with the saved destinationId and, for static mode, the private deployHook. The test must not start a build. CLI equivalents are blog astro preview and execute with --input. Never use Next.js connection tools for Astro. Existing Blog publishing, scheduling, and delivery reads apply after setup, with separate approval and publication permissions. Verify the exact live revision; a queued build is not publication. Browser Blog Settings remains the manual recovery path.

### Draft captions by destination

Resolve and preview the final caption for every selected destination before creating or updating a draft.

- Use the shared caption when it fits every destination and matches the user's intent. Do not create nearly identical overrides only to make each destination different.
- Create destination-specific overrides when platform limits, tone, formatting, or content type require different text. The CLI accepts `--instagram-caption`, `--tiktok-caption`, `--youtube-caption`, `--bluesky-caption`, and `--linkedin-caption`.
- Count each resolved caption using the destination's actual measurement rule and show `used / allowed` in the approval plan. Use a scalable `Caption usage` field rather than adding one table column per platform.
- Never create or update a draft with text that exceeds Rolino's validated destination limit. If text is too long, prepare a compliant revision before continuing. If shortening could materially change the message, show the revision to the user for approval.
- Treat preferred or “sweet spot” lengths as writing guidance, not hard limits, unless the user or their brand instructions make them requirements.

Current Rolino limits are Instagram 2,200 characters; TikTok captions 2,200 UTF-16 code units; YouTube descriptions 5,000 UTF-8 bytes; Bluesky posts 300 graphemes; and LinkedIn member posts 3,000 characters. Rolino's server validation is the source of truth if provider requirements change.

Use `rolino --agent posts show POST_ID --project PROJECT_ID` to obtain the current version before updating, scheduling, or publishing. Updates require `--expected-version` so web or teammate changes cannot be overwritten silently.

Updates are partial. Supply only fields the user intends to change; omitted media, destinations, captions, and provider settings are preserved. Changing TikTok caption, media, destinations, or its caption override invalidates the prior TikTok settings review.

### TikTok delivery settings

TikTok supports one video or up to 10 ordered photos. Photo originals must be JPEG, PNG, or WebP, up to 25 MiB each. Rolino makes JPEG delivery copies and keeps originals. The photoCoverIndex is zero-based and defaults to 0; keep allowDuet and allowStitch false and videoCoverTimestampMs null. autoAddMusic is optional, off by default, and only applies to direct photo posts. Photo inbox drafts include the caption; video inbox drafts do not. Refresh exact-account delivery options and review settings after content changes.

Refresh the connected creator's choices before creating or revising a TikTok draft:

~~~bash
rolino --agent integrations delivery-options --project PROJECT_ID --provider TIKTOK --account INTEGRATION_ID
~~~

Choose direct publishing or an inbox handoff explicitly. For direct publishing, use an exact returned visibility and explicit yes/no values for comments, duet, stitch, commercial content, own-brand promotion, third-party promotion, and AI-generated content. Never request an interaction that the refreshed result marks unavailable. Add `--tiktok-settings-reviewed` only after the choices were reviewed; this is separate from `--yes`, which confirms the Rolino draft mutation. Rolino stamps review time on the server and will not silently weaken a requested setting.

### LinkedIn member publishing

Refresh the connected member and supported post types before creating or revising a LinkedIn draft:

~~~bash
rolino --agent integrations delivery-options --project PROJECT_ID --provider LINKEDIN --account INTEGRATION_ID
~~~

LinkedIn supports text-only posts, up to ten stored JPEG/PNG images, or exactly one stored MP4 without image/video mixing. Native video must follow the refreshed delivery options; Rolino currently accepts 75 KB to 500 MB and 3 seconds to 30 minutes. Use `--linkedin-caption` for a destination-specific caption. Never infer that the developer app's associated Company Page is the publishing target: the author is always the member who completed OAuth. A successful publish execute only queues delivery, and native video remains in preparation until LinkedIn confirms upload and processing. If Rolino reports `LINKEDIN_PUBLISH_OUTCOME_UNKNOWN`, inspect the member's LinkedIn feed before any retry because the create request may have succeeded without returning an identifier.

## Local folder workflow

When the user supplies a folder of content:

1. Enumerate only that folder and identify supported JPEG, PNG, WebP, MP4, and MOV files. Ignore hidden, temporary, and unsupported files; do not rename, move, edit, or delete source files.
2. Determine the intended caption, destination, date, time, timezone, and ordering from explicit user instructions or an included manifest. Treat filenames as hints, not authoritative publishing instructions.
3. List existing project media and match obvious duplicates by exact file name and size when possible. Never substitute a merely similar asset.
4. Present one compact batch plan containing every source file, resolved destination caption, caption usage, destination, and exact scheduled time. Surface missing or over-limit captions, collisions, unsupported formats, provider constraints, and disconnected channels.
5. After the user approves that exact plan, upload only the approved files. Reuse returned asset IDs to create drafts, then run readiness on every draft.
6. Preview every exact schedule. If the previews match the approved batch with no new blockers or changes, the earlier explicit batch approval may cover execution. Otherwise show the changed result and ask again.
7. Execute each confirmed schedule with a stable, unique idempotency key, then verify the affected calendar window and report per-post results. A partial failure must not cause successful items to be duplicated.

Example upload and draft preparation:

~~~bash
rolino --agent media list --project PROJECT_ID --query "launch"
rolino --agent media upload "/exact/path/launch-01.mp4" --project PROJECT_ID --yes
rolino --agent posts create --project PROJECT_ID --caption "APPROVED TEXT" --platform TIKTOK --media ASSET_ID --tiktok-mode direct --tiktok-visibility RETURNED_VALUE --tiktok-comments no --tiktok-duet no --tiktok-stitch no --tiktok-commercial-content no --tiktok-promotes-own-brand no --tiktok-promotes-third-party no --tiktok-ai-generated no --tiktok-settings-reviewed --yes
~~~

## Schedule workflow

Scheduling is always two-step and exact:

~~~bash
rolino --agent posts readiness POST_ID --project PROJECT_ID
rolino --agent posts schedule preview POST_ID --project PROJECT_ID --expected-version VERSION --at ISO_DATE --timezone IANA_TIMEZONE
~~~

The preview returns a five-minute confirmation token. After the user approves the exact preview, pass the token through ROLINO_CONFIRMATION_TOKEN and repeat the same post version, time, and timezone with a stable idempotency key:

~~~bash
rolino --agent posts schedule execute POST_ID --project PROJECT_ID --expected-version VERSION --at ISO_DATE --timezone IANA_TIMEZONE --idempotency-key STABLE_KEY
~~~

Never treat a preview as a completed schedule. Never change the time, timezone, post version, or destinations between preview and execution.

For YouTube schedules, require Public in the draft settings. Confirmed scheduling starts the upload immediately as Private so YouTube can process it before the target time. A `PENDING` result with phase `REMOTE_SCHEDULE_CONFIRMATION`, or destination stage `CONFIRMING_SCHEDULE`, means the video upload is complete and YouTube is acknowledging the requested future Public publish time. It does not mean the video is public, and it is not a second publish action.

## Existing delivery recovery

Read saved remotePostId, remoteContainerId, and studioUrl in the post result. Before retrying an uncertain delivery, use check_post_delivery through MCP or:

~~~bash
rolino --agent posts recovery check POST_ID --project PROJECT_ID --provider YOUTUBE
~~~

This requires posts:write because it updates Rolino from provider evidence. It cannot upload, publish, or change visibility. UNKNOWN requires manual review. A missing ID or URL does not prove that no upload exists.

For an existing YouTube video, select the matching preview/execute pair: attach_video, reschedule_video, or publish_existing_video. These tools do not accept an action selector. Attachment has no publishAt; rescheduling requires a future publishAt; immediate publication requires publishAt: null. The CLI retains its action-based commands:

~~~bash
rolino --agent posts recovery preview POST_ID --project PROJECT_ID --expected-version VERSION --action ATTACH --video-id VIDEO_ID
rolino --agent posts recovery execute POST_ID --project PROJECT_ID --expected-version VERSION --action ATTACH --video-id VIDEO_ID --idempotency-key STABLE_KEY --yes
~~~

Show the preview's video, account, and consequence to the user. Execute only after approval. Supply its token through ROLINO_CONFIRMATION_TOKEN. ATTACH links the existing video and does not publish; check delivery afterward. To change the existing video's schedule, use RESUME with either --at ISO_DATE or --publish-now. In API or MCP input, RESUME requires publishAt as an explicit future UTC timestamp or null for publication now; ATTACH must omit it. Keep action, video ID, publication time, post version, and token identical between preview and execute. Reuse the same idempotency key on retries.

Recovery requires posts:write. RESUME also requires posts:schedule for a future time or posts:publish for publication now. QUEUED means the change is pending, not that the video is public. Recovery affects only the selected destination and never uploads another copy. Other providers support delivery checks according to their API permissions; unsupported checks remain unresolved.

## Immediate publish workflow

Immediate publishing is also two-step:

~~~bash
rolino --agent posts readiness POST_ID --project PROJECT_ID
rolino --agent posts publish preview POST_ID --project PROJECT_ID --expected-version VERSION --platform INSTAGRAM
~~~

After the user approves the exact destinations and consequence, pass the returned token through ROLINO_CONFIRMATION_TOKEN and execute with the same version and destinations:

~~~bash
rolino --agent posts publish execute POST_ID --project PROJECT_ID --expected-version VERSION --platform INSTAGRAM --idempotency-key STABLE_KEY
~~~

Use `--platform LINKEDIN` for an approved LinkedIn member destination; keep the preview and execute destination sets identical.

For immediate YouTube publishing, preserve the user's exact Public, Unlisted, or Private setting. The preview reports `deliveryMode: IMMEDIATE`, the configured visibility, and `createsSchedule: false`. Uploading and processing may continue after execute returns, but Rolino must not create or confirm a YouTube schedule for this flow.

A successful execute queues delivery; it is not proof that every provider has published. Inspect each destination's `deliveryMode` and `stage` afterward and report upload, processing, schedule confirmation, and publication accurately.

## Recovery rules

- `AUTH_REQUIRED`: ask the user to create or sign into a Rolino account and approve `rolino auth login` in the browser.
- `FORBIDDEN` or `SCOPE_REQUIRED`: explain which requested action lacks permission. The user must revoke or log out and authorize a new connection with that capability; never work around the scope.
- `BILLING_REQUIRED` or `LIMIT_REACHED`: report the plan restriction and direct the user to `https://getrolino.com/settings/billing`.
- `CONNECTION_REQUIRED` or a blocking integration-health result: direct the user to the project's Integrations page. OAuth and platform approval are user actions.
- `VALIDATION_ERROR`: preserve the server message, inspect the current post and readiness, and ask only for the missing decision or content.
- `CONFLICT`: fetch the latest resource before retrying. Never blind-retry with changed input under the same idempotency key.
- `RATE_LIMITED`: respect retryAfterSeconds.
- Network timeout or unknown provider result: inspect current state before retrying. Never assume a publish failed and send a duplicate.
- `LINKEDIN_PUBLISH_OUTCOME_UNKNOWN`: do not retry automatically or manually until the user checks the connected member's LinkedIn feed. Image initialization or upload failures occur before post creation and may follow Rolino's bounded safe retry path.

## Safety

- Reading, listing, health checks, readiness checks, and previews are safe to run without extra confirmation.
- Project and draft creation are writes; use Rolino's explicit consent flow and report what was created.
- Local file upload is a write and transfers a copy to the configured Rolino media store. Read and upload only files covered by the user's approved folder plan; never alter the originals.
- Scheduling and publishing affect external accounts. Require exact preview approval and never bypass Rolino's server confirmation.
- Do not expose bearer credentials or confirmation tokens in source code, command arguments when an environment variable is available, logs, or final responses.
- Do not claim success from intent. Verify the returned post, schedule, calendar event, or provider status and report blockers plainly.
