# output_public_url

Canonical HTML page: https://bitterclip.com/docs/assistants/tools/output_public_url

Surface: default model and app

Product release: 1dce717408a55fac1047e7d779177fa3ef8ede51
Public contract commit: 1ce47f7857ae3d9b6933fe83d5c33de3ae5efc64
Public contract SHA-256: 241d47d25e4b4c1dae9d2263793874d06fc4ba7bc3c78d4cf2bc63fbb32ed7dc
Descriptor SHA-256: 885d46f3ca3939dec2ace0cd6e93611c8b3f741f2d8f7a82e0adfe1452cc9a9e
Captured: 2026-09-24T16:39:30.100Z
Source: https://app.bitterclip.com/api/v1/mcp_descriptors.json

Create expiring public review and direct-download URLs for one exact finished BitterClip render. The review URL is the practical human handoff; download_url remains the primitive for external services that ingest a public video URL (captioning/styling connectors like Submagic, Zapier steps, publishing tools). Pass exactly ONE target: episode_id when you have an Episode/derived-clip handle, OR render_id when you only have the ready render artifact; never pass both. A derived clip's comp_... handle goes in episode_id. This tool never starts or retries rendering: if render_not_ready, call render_create and wait for render_status to report ready, then retry with one target. Repeating the call for the same ready render is safe and does not enqueue or mutate a render; it only returns fresh expiring bearer URLs. A later render invalidates these links rather than silently showing a different cut. When a UI supplied expected_render_generation, pass it back so a stale card fails instead of sharing a newer Render. Pair download_url with title and dictionary when the receiving service wants context.

The JSON blocks below are the exact MCP descriptor projections served by Rails for the named profiles at capture time. Security schemes and resource URIs may vary by connected host.

## Default model descriptor

```json
{
  "name": "output_public_url",
  "title": "Create public MP4 review and download links",
  "description": "Create expiring public review and direct-download URLs for one exact finished BitterClip render. The review URL is the practical human handoff; download_url remains the primitive for external services that ingest a public video URL (captioning/styling connectors like Submagic, Zapier steps, publishing tools). Pass exactly ONE target: episode_id when you have an Episode/derived-clip handle, OR render_id when you only have the ready render artifact; never pass both. A derived clip's comp_... handle goes in episode_id. This tool never starts or retries rendering: if render_not_ready, call render_create and wait for render_status to report ready, then retry with one target. Repeating the call for the same ready render is safe and does not enqueue or mutate a render; it only returns fresh expiring bearer URLs. A later render invalidates these links rather than silently showing a different cut. When a UI supplied expected_render_generation, pass it back so a stale card fails instead of sharing a newer Render. Pair download_url with title and dictionary when the receiving service wants context.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "episode_id": {
        "type": "string",
        "description": "Use this single target when you have an Episode or derived-clip composition ID such as comp_...; recording handles src_... resolve to their episode-of-one. Do not also pass render_id."
      },
      "render_id": {
        "type": "string",
        "description": "Use this single target only when you have the ready rart_... render artifact from render_status. Do not also pass episode_id."
      },
      "expires_in_days": {
        "type": "integer",
        "minimum": 1,
        "maximum": 14,
        "description": "Link lifetime in days. Default 7; minimum 1, maximum 14."
      },
      "expected_render_generation": {
        "type": "string",
        "description": "Optional opaque optimistic precondition from an Outputs result. If that exact rendered generation changed, this call fails instead of sharing a newer cut."
      }
    },
    "required": [],
    "additionalProperties": false,
    "oneOf": [
      {
        "required": [
          "episode_id"
        ],
        "not": {
          "required": [
            "render_id"
          ]
        }
      },
      {
        "required": [
          "render_id"
        ],
        "not": {
          "required": [
            "episode_id"
          ]
        }
      }
    ]
  },
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": false,
    "idempotentHint": true,
    "openWorldHint": true
  },
  "securitySchemes": [
    {
      "type": "noauth"
    }
  ],
  "_meta": {
    "securitySchemes": [
      {
        "type": "noauth"
      }
    ],
    "ui": {
      "visibility": [
        "model",
        "app"
      ]
    },
    "openai/widgetAccessible": true
  }
}
```

## App descriptor

```json
{
  "name": "output_public_url",
  "title": "Create public MP4 review and download links",
  "description": "Create expiring public review and direct-download URLs for one exact finished BitterClip render. The review URL is the practical human handoff; download_url remains the primitive for external services that ingest a public video URL (captioning/styling connectors like Submagic, Zapier steps, publishing tools). Pass exactly ONE target: episode_id when you have an Episode/derived-clip handle, OR render_id when you only have the ready render artifact; never pass both. A derived clip's comp_... handle goes in episode_id. This tool never starts or retries rendering: if render_not_ready, call render_create and wait for render_status to report ready, then retry with one target. Repeating the call for the same ready render is safe and does not enqueue or mutate a render; it only returns fresh expiring bearer URLs. A later render invalidates these links rather than silently showing a different cut. When a UI supplied expected_render_generation, pass it back so a stale card fails instead of sharing a newer Render. Pair download_url with title and dictionary when the receiving service wants context.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "episode_id": {
        "type": "string",
        "description": "Use this single target when you have an Episode or derived-clip composition ID such as comp_...; recording handles src_... resolve to their episode-of-one. Do not also pass render_id."
      },
      "render_id": {
        "type": "string",
        "description": "Use this single target only when you have the ready rart_... render artifact from render_status. Do not also pass episode_id."
      },
      "expires_in_days": {
        "type": "integer",
        "minimum": 1,
        "maximum": 14,
        "description": "Link lifetime in days. Default 7; minimum 1, maximum 14."
      },
      "expected_render_generation": {
        "type": "string",
        "description": "Optional opaque optimistic precondition from an Outputs result. If that exact rendered generation changed, this call fails instead of sharing a newer cut."
      }
    },
    "required": [],
    "additionalProperties": false,
    "oneOf": [
      {
        "required": [
          "episode_id"
        ],
        "not": {
          "required": [
            "render_id"
          ]
        }
      },
      {
        "required": [
          "render_id"
        ],
        "not": {
          "required": [
            "episode_id"
          ]
        }
      }
    ]
  },
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": false,
    "idempotentHint": true,
    "openWorldHint": true
  },
  "securitySchemes": [
    {
      "type": "noauth"
    }
  ],
  "_meta": {
    "securitySchemes": [
      {
        "type": "noauth"
      }
    ],
    "ui": {
      "visibility": [
        "model",
        "app"
      ]
    },
    "openai/widgetAccessible": true
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "review_url": {
        "type": "string",
        "description": "Public, expiring review page for this exact Render."
      },
      "download_url": {
        "type": "string",
        "description": "Public, expiring direct-download MP4 URL."
      },
      "watch_url": {
        "type": "string",
        "description": "Compatibility alias for review_url."
      },
      "title": {
        "type": "string",
        "description": "Episode/clip title, for the receiving service."
      },
      "content_type": {
        "type": "string",
        "enum": [
          "video/mp4"
        ]
      },
      "duration_seconds": {
        "type": [
          "number",
          "null"
        ]
      },
      "size_bytes": {
        "type": [
          "integer",
          "null"
        ]
      },
      "render_generation": {
        "type": "string",
        "description": "Opaque identity for the exact rendered bytes and poster bound to these URLs."
      },
      "expires_at": {
        "type": "string",
        "description": "ISO8601 expiry timestamp."
      },
      "dictionary": {
        "type": "array",
        "items": {
          "type": "string"
        },
        "description": "Account, project, speaker, and brand names a captioning service should preserve."
      }
    },
    "required": [],
    "additionalProperties": false
  }
}
```

## Live Workspace descriptor

```json
{
  "name": "output_public_url",
  "title": "Create public MP4 review and download links",
  "description": "Create expiring public review and direct-download URLs for one exact finished BitterClip render. The review URL is the practical human handoff; download_url remains the primitive for external services that ingest a public video URL (captioning/styling connectors like Submagic, Zapier steps, publishing tools). Pass exactly ONE target: episode_id when you have an Episode/derived-clip handle, OR render_id when you only have the ready render artifact; never pass both. A derived clip's comp_... handle goes in episode_id. This tool never starts or retries rendering: if render_not_ready, call render_create and wait for render_status to report ready, then retry with one target. Repeating the call for the same ready render is safe and does not enqueue or mutate a render; it only returns fresh expiring bearer URLs. A later render invalidates these links rather than silently showing a different cut. When a UI supplied expected_render_generation, pass it back so a stale card fails instead of sharing a newer Render. Pair download_url with title and dictionary when the receiving service wants context.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "episode_id": {
        "type": "string",
        "description": "Use this single target when you have an Episode or derived-clip composition ID such as comp_...; recording handles src_... resolve to their episode-of-one. Do not also pass render_id."
      },
      "render_id": {
        "type": "string",
        "description": "Use this single target only when you have the ready rart_... render artifact from render_status. Do not also pass episode_id."
      },
      "expires_in_days": {
        "type": "integer",
        "minimum": 1,
        "maximum": 14,
        "description": "Link lifetime in days. Default 7; minimum 1, maximum 14."
      },
      "expected_render_generation": {
        "type": "string",
        "description": "Optional opaque optimistic precondition from an Outputs result. If that exact rendered generation changed, this call fails instead of sharing a newer cut."
      }
    },
    "required": [],
    "additionalProperties": false,
    "oneOf": [
      {
        "required": [
          "episode_id"
        ],
        "not": {
          "required": [
            "render_id"
          ]
        }
      },
      {
        "required": [
          "render_id"
        ],
        "not": {
          "required": [
            "episode_id"
          ]
        }
      }
    ]
  },
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": false,
    "idempotentHint": true,
    "openWorldHint": true
  },
  "securitySchemes": [
    {
      "type": "noauth"
    }
  ],
  "_meta": {
    "securitySchemes": [
      {
        "type": "noauth"
      }
    ]
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "review_url": {
        "type": "string",
        "description": "Public, expiring review page for this exact Render."
      },
      "download_url": {
        "type": "string",
        "description": "Public, expiring direct-download MP4 URL."
      },
      "watch_url": {
        "type": "string",
        "description": "Compatibility alias for review_url."
      },
      "title": {
        "type": "string",
        "description": "Episode/clip title, for the receiving service."
      },
      "content_type": {
        "type": "string",
        "enum": [
          "video/mp4"
        ]
      },
      "duration_seconds": {
        "type": [
          "number",
          "null"
        ]
      },
      "size_bytes": {
        "type": [
          "integer",
          "null"
        ]
      },
      "render_generation": {
        "type": "string",
        "description": "Opaque identity for the exact rendered bytes and poster bound to these URLs."
      },
      "expires_at": {
        "type": "string",
        "description": "ISO8601 expiry timestamp."
      },
      "dictionary": {
        "type": "array",
        "items": {
          "type": "string"
        },
        "description": "Account, project, speaker, and brand names a captioning service should preserve."
      },
      "workspace_binding": {
        "type": "object",
        "properties": {
          "schema": {
            "type": "string",
            "enum": [
              "bitterclip.workspace-binding.v2"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "live_workspace"
            ]
          },
          "project_id": {
            "type": "string"
          },
          "target_kind": {
            "type": "string",
            "enum": [
              "episode",
              "clip"
            ]
          },
          "target_id": {
            "type": "string"
          },
          "revision": {
            "type": "integer"
          },
          "draft_revision": {
            "type": "string"
          },
          "workspace_url": {
            "type": "string"
          },
          "output_id": {
            "type": "string"
          },
          "output_status": {
            "type": "string",
            "enum": [
              "missing",
              "stale",
              "rendering",
              "ready",
              "failed"
            ]
          },
          "output_revision": {
            "type": "integer"
          },
          "parent_episode_id": {
            "type": "string"
          }
        },
        "required": [
          "schema",
          "mode",
          "target_kind",
          "target_id",
          "revision",
          "draft_revision",
          "workspace_url",
          "output_status"
        ],
        "additionalProperties": false
      }
    },
    "required": [],
    "additionalProperties": false
  }
}
```

## Errors

```json
[
  "render_not_ready",
  "render_output_unavailable",
  "render_changed",
  "not_found",
  "forbidden",
  "invalid_input"
]
```

## Examples

```json
[
  {
    "label": "Handoff a ready Episode or derived clip",
    "input": {
      "episode_id": "comp_...",
      "expires_in_days": 7
    }
  },
  {
    "label": "Handoff when only the ready render artifact is in context",
    "input": {
      "render_id": "rart_...",
      "expires_in_days": 7
    }
  }
]
```
