Skip to main content
Glama

Create Bulk Purchase

create_bulk_purchase
Destructive

Place bulk Wistia media purchase orders for captions, localizations, audio descriptions, or transcript translations. Tracks async progress and reports per-order results.

Instructions

Submits either an actions array of up to 1000 orders or one job that can resolve to up to 5000 media. Orders are placed asynchronously. Returns a background job status whose Show endpoint reports aggregate progress and per-order results.

Orders in the batch can incur charges, so a saved credit card is required. Supported resource types are captions (Wistia-generated English captions), localization (a dubbed, language-specific version of a media), extended_audio_description, and text_translation (the media's transcript translated into another language, audio untouched). Each order's id is the hashed ID of the media to order for.

What an order costs depends on the account, not on this endpoint. Automated captions are included at no cost on plans that provide them and billed at the account's configured per-minute rate otherwise; human-reviewed captions bill per minute at the account's standard or rush rate; localizations bill per minute once the account's free-dub allowance is used up; text translations bill as an overage once the account's included translation minutes are used up. Check the account's plan and billing settings for its actual rates.

Orders are priced and placed individually: failures -- an ineligible media, a language that already has a localization, an account not entitled to buy -- are reported per order and do not stop the rest of the batch. Pricing and eligibility match the equivalent single-media endpoints exactly.

Use the Create Bulk Actions endpoint for create, update, and delete work; it does not accept purchase, and this endpoint accepts nothing else.

Requires api token with one of the following permissions

Read, update & delete anything

Tokens with the "Act with a team member's permissions" permission (all:delegate_to_contact_permissions scope) can also be used. Requests made with such a token are authorized using the permissions of the contact assigned to the token. Requires confirm=true for the requested mutation. May share access, notify people or incur provider charges.

Input Schema

NameRequiredDescriptionDefault
jobNo
accountNoNamed private Wistia account; selects credentials, not a remote account ID.
actionsNo
confirmNoSet true only when the user asked for exactly this action.
payloadNoComplete JSON request body instead of body flags. Supports current nested customization, caption and nullable values.
payload_fileNoRegular local JSON body file, at most 5 MB. Cannot be mixed with body flags or payload.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed26 schema fields changedv3.0.0
    • addedInput schema / $defs
      Added value: +{
      +  "actions": {
      +    "description": "The orders to place, one per media. Maximum 1000 per request, and the\nrequest body must stay under 2 MB -- whichever limit is reached first. An\noversized body is rejected with a `413` and no order in it is placed.\n\nEvery order is priced and placed independently: one failing (an\nineligible media, an account without a saved card, a language that\nalready has a localization) does not stop the rest of the batch.\n\nUse `job` instead to order for a whole folder, channel, or account.\n",
      +    "items": {
      +      "description": "A single paid order within a bulk purchase request.",
      +      "properties": {
      +        "id": {
      +          "description": "The hashed ID of the media to order for. Always the media's own ID: what\nthe order produces does not exist yet.\n",
      +          "type": "string"
      +        },
      +        "operation": {
      +          "description": "Always `purchase`. This endpoint places orders only; to create, update,\nor delete records in bulk use the Create Bulk Actions endpoint, which\ndoes not accept `purchase`.\n",
      +          "enum": [
      +            "purchase"
      +          ],
      +          "type": "string"
      +        },
      +        "payload": {
      +          "description": "Order options. The accepted fields depend on the resource type and match\nthe corresponding single-media endpoint's request body. Omit it to take\nevery default.\n\n`captions` accepts `automated` (order computer-generated captions\ninstead of human-reviewed ones), `rush` (one business day turnaround\ninstead of four, human-reviewed only, at a higher per-minute rate), and\n`automatically_enable` (show the captions on the video as soon as they\nare ready). Each is treated as `false` when omitted or unrecognized.\nWhat each option costs depends on the account's plan and billing\nsettings.\n\n`localization` requires `output_language`, a 3-character IETF language\ncode, and accepts `auto_enable` (default `true`).\n\n`extended_audio_description` accepts `enabled` (default `true`),\n`ai_enabled` (default `true`), `ietf_language_tag` (default `eng`), and\n`order_instructions`.\n\n`text_translation` requires `target_language` and accepts\n`source_language` (which transcript to translate from, defaulting to the\nmedia's own language). Use the bibliographic ISO 639-2 form or a\nsupported regional or script IETF tag for either value.\n",
      +          "type": "object",
      +          "unevaluatedProperties": true
      +        },
      +        "resource_type": {
      +          "$ref": "#/$defs/resource_type"
      +        }
      +      },
      +      "required": [
      +        "operation",
      +        "resource_type",
      +        "id"
      +      ],
      +      "type": "object",
      +      "unevaluatedProperties": false
      +    },
      +    "maxItems": 1000,
      +    "minItems": 1,
      +    "type": "array"
      +  },
      +  "job": {
      +    "description": "One order placed for many media, named by a parent (`scope`) or listed\nexplicitly (`ids`), so ordering captions for a folder of 47 videos takes one\njob rather than 47 orders.\n\nA `scope` resolves to exactly what List Media returns for that parent,\nincluding media in the folder's subfolders and **archived** media, and to at\nmost 5000 media -- beyond that the job is rejected rather than truncated.\n\nThe job attempts one order for every media it resolves to. Ineligible media\nfail individually without placing an order; successful orders are metered\nand may incur charges according to the account's plan. Confirm the scope and\npotential cost with the customer before submitting. Not available to external\ncontacts.\n",
      +    "properties": {
      +      "ids": {
      +        "description": "The media to order for, named explicitly. Use this instead of `scope`\nwhen the media do not share a parent. Give either `scope` or `ids`, never\nboth.\n",
      +        "items": {
      +          "type": "string"
      +        },
      +        "maxItems": 1000,
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "operation": {
      +        "description": "Always `purchase`.",
      +        "enum": [
      +          "purchase"
      +        ],
      +        "type": "string"
      +      },
      +      "payload": {
      +        "description": "Order options applied to every matching media, in the same shape a single\norder's payload takes for this resource type.\n",
      +        "type": "object",
      +        "unevaluatedProperties": true
      +      },
      +      "resource_type": {
      +        "$ref": "#/$defs/resource_type"
      +      },
      +      "scope": {
      +        "description": "The parent whose media the order applies to. An order always addresses the\nmedia, so the valid parent types are the same for every resource type here.\n",
      +        "properties": {
      +          "id": {
      +            "description": "The parent's hashed ID. Required for every scope type except `account`.\n",
      +            "type": "string"
      +          },
      +          "type": {
      +            "description": "The kind of parent `id` names. Required, because a hashed ID does not say\nwhat it belongs to -- the same value could name a folder or a channel.\nUse `account` to order for every media in the account.\n",
      +            "enum": [
      +              "account",
      +              "folder",
      +              "subfolder",
      +              "channel"
      +            ],
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "type"
      +        ],
      +        "type": "object",
      +        "unevaluatedProperties": false
      +      }
      +    },
      +    "required": [
      +      "operation",
      +      "resource_type"
      +    ],
      +    "type": "object",
      +    "unevaluatedProperties": false
      +  },
      +  "resource_type": {
      +    "description": "What to order for the media.\n\n`captions` orders Wistia-generated English captions -- computer-generated\nor human-reviewed. `localization` orders a dubbed, language-specific\nversion of the media. `extended_audio_description` orders an extended\naudio description track. `text_translation` translates the media's\nexisting transcript into another language, leaving the audio alone.\n",
      +    "enum": [
      +      "captions",
      +      "localization",
      +      "extended_audio_description",
      +      "text_translation"
      +    ],
      +    "type": "string"
      +  }
      +}
    • addedInput schema / properties / actions / $ref
      Added value: +"#/$defs/actions"
    • removedInput schema / properties / actions / description
      Removed value: -"The orders to place, one per media. Maximum 1000 per request, and the\nrequest body must stay under 2 MB -- whichever limit is reached first. An\noversized body is rejected with a `413` and no order in it is placed.\n\nEvery order is priced and placed independently: one failing (an\nineligible media, an account without a saved card, a language that\nalready has a localization) does not stop the rest of the batch.\n\nUse `job` instead to order for a whole folder, channel, or account.\n"
    • removedInput schema / properties / actions / items
      Removed value: -{
      -  "description": "A single paid order within a bulk purchase request.",
      -  "properties": {
      -    "id": {
      -      "description": "The hashed ID of the media to order for. Always the media's own ID: what\nthe order produces does not exist yet.\n",
      -      "type": "string"
      -    },
      -    "operation": {
      -      "description": "Always `purchase`. This endpoint places orders only; to create, update,\nor delete records in bulk use the Create Bulk Actions endpoint, which\ndoes not accept `purchase`.\n",
      -      "enum": [
      -        "purchase"
      -      ],
      -      "type": "string"
      -    },
      -    "payload": {
      -      "description": "Order options. The accepted fields depend on the resource type and match\nthe corresponding single-media endpoint's request body. Omit it to take\nevery default.\n\n`captions` accepts `automated` (order computer-generated captions\ninstead of human-reviewed ones), `rush` (one business day turnaround\ninstead of four, human-reviewed only, at a higher per-minute rate), and\n`automatically_enable` (show the captions on the video as soon as they\nare ready). Each is treated as `false` when omitted or unrecognized.\nWhat each option costs depends on the account's plan and billing\nsettings.\n\n`localization` requires `output_language`, a 3-character IETF language\ncode, and accepts `auto_enable` (default `true`).\n\n`extended_audio_description` accepts `enabled` (default `true`),\n`ai_enabled` (default `true`), `ietf_language_tag` (default `eng`), and\n`order_instructions`.\n\n`text_translation` requires `target_language` and accepts\n`source_language` (which transcript to translate from, defaulting to the\nmedia's own language). Use the bibliographic ISO 639-2 form or a\nsupported regional or script IETF tag for either value.\n",
      -      "type": "object",
      -      "unevaluatedProperties": true
      -    },
      -    "resource_type": {
      -      "description": "What to order for the media.\n\n`captions` orders Wistia-generated English captions -- computer-generated\nor human-reviewed. `localization` orders a dubbed, language-specific\nversion of the media. `extended_audio_description` orders an extended\naudio description track. `text_translation` translates the media's\nexisting transcript into another language, leaving the audio alone.\n",
      -      "enum": [
      -        "captions",
      -        "localization",
      -        "extended_audio_description",
      -        "text_translation"
      -      ],
      -      "type": "string"
      -    }
      -  },
      -  "required": [
      -    "operation",
      -    "resource_type",
      -    "id"
      -  ],
      -  "type": "object",
      -  "unevaluatedProperties": false
      -}
    • removedInput schema / properties / actions / maxItems
      Removed value: -1000
    • removedInput schema / properties / actions / minItems
      Removed value: -1
    • removedInput schema / properties / actions / type
      Removed value: -"array"
    • changedInput schema / properties / confirm / description
      Previous value: -"Must be true for the specific user-requested write."New value: +"Set true only when the user asked for exactly this action."
    • addedInput schema / properties / job / $ref
      Added value: +"#/$defs/job"
    • removedInput schema / properties / job / description
      Removed value: -"One order placed for many media, named by a parent (`scope`) or listed\nexplicitly (`ids`), so ordering captions for a folder of 47 videos takes one\njob rather than 47 orders.\n\nA `scope` resolves to exactly what List Media returns for that parent,\nincluding media in the folder's subfolders and **archived** media, and to at\nmost 5000 media -- beyond that the job is rejected rather than truncated.\n\nThe job attempts one order for every media it resolves to. Ineligible media\nfail individually without placing an order; successful orders are metered\nand may incur charges according to the account's plan. Confirm the scope and\npotential cost with the customer before submitting. Not available to external\ncontacts.\n"
    • removedInput schema / properties / job / properties
      Removed value: -{
      -  "ids": {
      -    "description": "The media to order for, named explicitly. Use this instead of `scope`\nwhen the media do not share a parent. Give either `scope` or `ids`, never\nboth.\n",
      -    "items": {
      -      "type": "string"
      -    },
      -    "maxItems": 1000,
      -    "minItems": 1,
      -    "type": "array"
      -  },
      -  "operation": {
      -    "description": "Always `purchase`.",
      -    "enum": [
      -      "purchase"
      -    ],
      -    "type": "string"
      -  },
      -  "payload": {
      -    "description": "Order options applied to every matching media, in the same shape a single\norder's payload takes for this resource type.\n",
      -    "type": "object",
      -    "unevaluatedProperties": true
      -  },
      -  "resource_type": {
      -    "description": "What to order for the media.\n\n`captions` orders Wistia-generated English captions -- computer-generated\nor human-reviewed. `localization` orders a dubbed, language-specific\nversion of the media. `extended_audio_description` orders an extended\naudio description track. `text_translation` translates the media's\nexisting transcript into another language, leaving the audio alone.\n",
      -    "enum": [
      -      "captions",
      -      "localization",
      -      "extended_audio_description",
      -      "text_translation"
      -    ],
      -    "type": "string"
      -  },
      -  "scope": {
      -    "description": "The parent whose media the order applies to. An order always addresses the\nmedia, so the valid parent types are the same for every resource type here.\n",
      -    "properties": {
      -      "id": {
      -        "description": "The parent's hashed ID. Required for every scope type except `account`.\n",
      -        "type": "string"
      -      },
      -      "type": {
      -        "description": "The kind of parent `id` names. Required, because a hashed ID does not say\nwhat it belongs to -- the same value could name a folder or a channel.\nUse `account` to order for every media in the account.\n",
      -        "enum": [
      -          "account",
      -          "folder",
      -          "subfolder",
      -          "channel"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "type"
      -    ],
      -    "type": "object",
      -    "unevaluatedProperties": false
      -  }
      -}
    • removedInput schema / properties / job / required
      Removed value: -[
      -  "operation",
      -  "resource_type"
      -]
    • removedInput schema / properties / job / type
      Removed value: -"object"
    • removedInput schema / properties / job / unevaluatedProperties
      Removed value: -false
    • addedInput schema / properties / payload / properties / actions / $ref
      Added value: +"#/$defs/actions"
    • removedInput schema / properties / payload / properties / actions / description
      Removed value: -"The orders to place, one per media. Maximum 1000 per request, and the\nrequest body must stay under 2 MB -- whichever limit is reached first. An\noversized body is rejected with a `413` and no order in it is placed.\n\nEvery order is priced and placed independently: one failing (an\nineligible media, an account without a saved card, a language that\nalready has a localization) does not stop the rest of the batch.\n\nUse `job` instead to order for a whole folder, channel, or account.\n"
    • removedInput schema / properties / payload / properties / actions / items
      Removed value: -{
      -  "description": "A single paid order within a bulk purchase request.",
      -  "properties": {
      -    "id": {
      -      "description": "The hashed ID of the media to order for. Always the media's own ID: what\nthe order produces does not exist yet.\n",
      -      "type": "string"
      -    },
      -    "operation": {
      -      "description": "Always `purchase`. This endpoint places orders only; to create, update,\nor delete records in bulk use the Create Bulk Actions endpoint, which\ndoes not accept `purchase`.\n",
      -      "enum": [
      -        "purchase"
      -      ],
      -      "type": "string"
      -    },
      -    "payload": {
      -      "description": "Order options. The accepted fields depend on the resource type and match\nthe corresponding single-media endpoint's request body. Omit it to take\nevery default.\n\n`captions` accepts `automated` (order computer-generated captions\ninstead of human-reviewed ones), `rush` (one business day turnaround\ninstead of four, human-reviewed only, at a higher per-minute rate), and\n`automatically_enable` (show the captions on the video as soon as they\nare ready). Each is treated as `false` when omitted or unrecognized.\nWhat each option costs depends on the account's plan and billing\nsettings.\n\n`localization` requires `output_language`, a 3-character IETF language\ncode, and accepts `auto_enable` (default `true`).\n\n`extended_audio_description` accepts `enabled` (default `true`),\n`ai_enabled` (default `true`), `ietf_language_tag` (default `eng`), and\n`order_instructions`.\n\n`text_translation` requires `target_language` and accepts\n`source_language` (which transcript to translate from, defaulting to the\nmedia's own language). Use the bibliographic ISO 639-2 form or a\nsupported regional or script IETF tag for either value.\n",
      -      "type": "object",
      -      "unevaluatedProperties": true
      -    },
      -    "resource_type": {
      -      "description": "What to order for the media.\n\n`captions` orders Wistia-generated English captions -- computer-generated\nor human-reviewed. `localization` orders a dubbed, language-specific\nversion of the media. `extended_audio_description` orders an extended\naudio description track. `text_translation` translates the media's\nexisting transcript into another language, leaving the audio alone.\n",
      -      "enum": [
      -        "captions",
      -        "localization",
      -        "extended_audio_description",
      -        "text_translation"
      -      ],
      -      "type": "string"
      -    }
      -  },
      -  "required": [
      -    "operation",
      -    "resource_type",
      -    "id"
      -  ],
      -  "type": "object",
      -  "unevaluatedProperties": false
      -}
    • removedInput schema / properties / payload / properties / actions / maxItems
      Removed value: -1000
    • removedInput schema / properties / payload / properties / actions / minItems
      Removed value: -1
    • removedInput schema / properties / payload / properties / actions / type
      Removed value: -"array"
    • addedInput schema / properties / payload / properties / job / $ref
      Added value: +"#/$defs/job"
    • removedInput schema / properties / payload / properties / job / description
      Removed value: -"One order placed for many media, named by a parent (`scope`) or listed\nexplicitly (`ids`), so ordering captions for a folder of 47 videos takes one\njob rather than 47 orders.\n\nA `scope` resolves to exactly what List Media returns for that parent,\nincluding media in the folder's subfolders and **archived** media, and to at\nmost 5000 media -- beyond that the job is rejected rather than truncated.\n\nThe job attempts one order for every media it resolves to. Ineligible media\nfail individually without placing an order; successful orders are metered\nand may incur charges according to the account's plan. Confirm the scope and\npotential cost with the customer before submitting. Not available to external\ncontacts.\n"
    • removedInput schema / properties / payload / properties / job / properties
      Removed value: -{
      -  "ids": {
      -    "description": "The media to order for, named explicitly. Use this instead of `scope`\nwhen the media do not share a parent. Give either `scope` or `ids`, never\nboth.\n",
      -    "items": {
      -      "type": "string"
      -    },
      -    "maxItems": 1000,
      -    "minItems": 1,
      -    "type": "array"
      -  },
      -  "operation": {
      -    "description": "Always `purchase`.",
      -    "enum": [
      -      "purchase"
      -    ],
      -    "type": "string"
      -  },
      -  "payload": {
      -    "description": "Order options applied to every matching media, in the same shape a single\norder's payload takes for this resource type.\n",
      -    "type": "object",
      -    "unevaluatedProperties": true
      -  },
      -  "resource_type": {
      -    "description": "What to order for the media.\n\n`captions` orders Wistia-generated English captions -- computer-generated\nor human-reviewed. `localization` orders a dubbed, language-specific\nversion of the media. `extended_audio_description` orders an extended\naudio description track. `text_translation` translates the media's\nexisting transcript into another language, leaving the audio alone.\n",
      -    "enum": [
      -      "captions",
      -      "localization",
      -      "extended_audio_description",
      -      "text_translation"
      -    ],
      -    "type": "string"
      -  },
      -  "scope": {
      -    "description": "The parent whose media the order applies to. An order always addresses the\nmedia, so the valid parent types are the same for every resource type here.\n",
      -    "properties": {
      -      "id": {
      -        "description": "The parent's hashed ID. Required for every scope type except `account`.\n",
      -        "type": "string"
      -      },
      -      "type": {
      -        "description": "The kind of parent `id` names. Required, because a hashed ID does not say\nwhat it belongs to -- the same value could name a folder or a channel.\nUse `account` to order for every media in the account.\n",
      -        "enum": [
      -          "account",
      -          "folder",
      -          "subfolder",
      -          "channel"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "type"
      -    ],
      -    "type": "object",
      -    "unevaluatedProperties": false
      -  }
      -}
    • removedInput schema / properties / payload / properties / job / required
      Removed value: -[
      -  "operation",
      -  "resource_type"
      -]
    • removedInput schema / properties / payload / properties / job / type
      Removed value: -"object"
    • removedInput schema / properties / payload / properties / job / unevaluatedProperties
      Removed value: -false
  2. First observedv2.0.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructive/non-idempotent/openWorld, but the description adds substantial context beyond them: asynchronous placement, the background job status endpoint that reports per-order results, the saved-credit-card requirement, per-order failure isolation, and the cost model. It even warns to confirm scope and potential cost with the customer before submitting.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core behavior (actions vs job, async, charges) and permissions before the detail. The billing paragraph is lengthy and somewhat tangential to invocation, but the overall structure is deliberate and readable for a high-complexity tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex, charge-incurring, no-output-schema mutation tool, the description covers return behavior (job status endpoint), auth/permission requirements, confirmation, cost, and failure semantics. Nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 67% and the nested $defs are themselves richly documented, but the description still adds meaning: the 1000-order vs 5000-media ceiling, that each order's `id` is the hashed media ID, and what each resource_type produces. It does not re-explain payload option fields, which live only in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Submits ... actions array of up to 1000 orders or one job that can resolve to up to 5000 media') and immediately differentiates itself from the sibling Create Bulk Actions endpoint, which it explicitly says does not accept `purchase`. An agent can distinguish this from create_bulk_actions, purchase_captions, and create_localization without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly describes when to use `actions` (named media, one order each) versus `job` (a scope resolving to a whole folder/channel/account), names the alternative endpoint for create/update/delete work, and states the confirm=true prerequisite. Conditions and exclusions are all spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools