Viewcast batch measurement purchase

Buy up to 100 measurements in one call with POST /v2/viewcast/measurements/purchase/{draft,final,direct}/batch: the batch body with its whole-batch idempotency key, per-item outcomes in submission order, pricing where only queued items bill, and the per-type rules for draft, final, and direct items.

The batch measurement purchase endpoints buy measurements for 1 to 100 targets in one synchronous call. Instead of looping the single-purchase endpoint client-side, you submit the whole list in one request and get back one outcome per item, in your submission order. One call buys one purchase type: the type (draft, final, or direct) sits in the path, and every item runs exactly the single-purchase flow for that type. One item's failure never blocks another.

POST /v2/viewcast/measurements/purchase/{draft,final,direct}/batch

The endpoints are on the v2 base https://api2.mworks.com. For the endpoint-by-endpoint reference, see the Viewcast API reference. For the draft and final single purchases on the same route family, see Displays On Demand measurements. For the single-item direct path, see Viewcast direct measurement purchase.

When to use it

Use the batch surface when you are buying for many targets at once:

  • a list of asset_ids you own, buying draft or final measurements for all of them, or
  • a list of display_ids from inventory search, buying them all directly.

For a single target, use the single-purchase endpoints: same flow, less ceremony.

Request

The body has two fields: a whole-batch idempotency key and the item list.

FieldTypeRequiredMeaning
purchase_request_idUUIDYesYour idempotency identifier for the whole batch (see below).
itemsArray of 1 to 100 itemsYesThe items to purchase, processed in submission order.

Each item carries the target plus a per-item idempotency id. draft and final items take owned assets:

FieldTypeRequiredMeaning
asset_idStringYesAn asset owned by your organization.
measurement_request_idUUIDYesPer-item idempotency identifier.

direct items take display identifiers:

FieldTypeRequiredMeaning
display_idStringYesThe display to purchase, as issued by the Motionworks display catalog.
measurement_request_idUUIDYesPer-item idempotency identifier.

The body is strict: unknown keys return 400 naming the rejected field, and so do an empty item list, more than 100 items, non-UUID identifiers, the retired kind body field, and the Idempotency-Key header (batch idempotency lives in purchase_request_id).

curl -X POST https://api2.mworks.com/v2/viewcast/measurements/purchase/draft/batch \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{
    "purchase_request_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "items": [
      {
        "asset_id": "vcaJDVDYWSSMHAXVEFYW1DVD2PPFD",
        "measurement_request_id": "550e8400-e29b-41d4-a716-446655440000"
      },
      {
        "asset_id": "vcaQTWJBXSKGHFL4Z1MV9DPWRX",
        "measurement_request_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
      }
    ]
  }'

Response

The call is synchronous: every item is processed before the response returns. A successful call returns 202 Accepted with one outcome per item in your submission order. Queued draft items then complete through the normal measurement pipeline; poll them as usual (see Displays On Demand measurements).

{
  "data": {
    "purchase_request_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "type": "draft",
    "submitted_at": "2026-09-13T14:02:11Z",
    "completed_at": "2026-09-13T14:02:14Z",
    "items": [
      {
        "target": "vcaJDVDYWSSMHAXVEFYW1DVD2PPFD",
        "outcome": "queued",
        "measurement_id": "9c8b7a6b-5d4e-4f3a-8b2c-1d0e9f8a7b6c",
        "processed_at": "2026-09-13T14:02:12Z"
      },
      {
        "target": "vcaQTWJBXSKGHFL4Z1MV9DPWRX",
        "outcome": "skipped:not_owned",
        "processed_at": "2026-09-13T14:02:13Z"
      }
    ],
    "queued_count": 1
  },
  "meta": {
    "request_id": "req_...",
    "credits_used": 150,
    "product": "viewcast"
  }
}

Each item echoes its target (asset_id for draft and final, display_id for direct) and carries a processed_at terminal timestamp. Queued items carry the minted measurement_id; queued direct items also carry the minted asset_id. The full outcome set:

OutcomeBilledMeaning
queuedYesThe item charged and queued exactly as the single-purchase flow.
skipped:not_ownedNoThe asset_id is not owned by your organization.
skipped:bad_stateNoThe asset's current state does not allow this purchase type.
skipped:already_measuredNoThe asset already holds the measurement this purchase would queue.
skipped:idempotent_replayNoThe item's per-item replay key was already recorded; the recorded outcome returns at no charge.
skipped:not_in_catalogNoDirect only: the catalog has no entry for the display_id.
skipped:already_licensedNoDirect only: your organization already licenses the display_id.
skipped:duplicate_in_batchNoThe same target appears twice in this batch; only the first occurrence is processed.
failed:insufficient_creditsNoThe wallet could not fund this item's charge (the per-item 402); the batch continued past it.
failed:not_availableNoThe purchase could not run; see the final-type posture below.
failed:upstreamNoAn upstream dependency failed while processing the item.

Pricing

The call costs a 1 credit base (once per call) plus the per-item purchase delta for every item that queues: 149 credits per queued draft item, 299 credits per queued final or direct item. A queued item therefore costs exactly the single-purchase total: 150 credits for a draft item, 300 credits for a final or direct item. Total charge = 1 + (rate - 1) x queued_count.

Skipped and failed items bill nothing. There is no whole-call wallet pre-check: each item charges on its own, and an item your wallet cannot fund surfaces as failed:insufficient_credits while the rest of the batch continues. The example response above charged 150 credits: one queued draft item plus the 1 credit base.

Idempotency and replay

purchase_request_id (any RFC 4122 UUID, minted client-side) is the idempotency key for the whole batch, and each item carries its own measurement_request_id:

  • Same purchase_request_id + same item list in the same order: returns the recorded outcomes at 0 credits, with the header X-MW-Idempotent-Replay: 1. Nothing is queued or charged a second time.
  • Same id + a different item list (or the same items reordered): returns 409 idempotency_mismatch. The mismatch is rejected before anything is charged.
  • Same id while a batch with that id is still processing: returns 409; retry shortly.

Per-item replay keys are derived server-side as <purchase_request_id>:<measurement_request_id>, so a batch retried under the same purchase_request_id never double-bills an item that already queued: on reprocessing, such an item reports skipped:idempotent_replay at no charge.

Mint a fresh purchase_request_id for each genuinely new batch, and a fresh measurement_request_id for each item in it.

Type-specific behavior

  • draft: every item runs the single draft-purchase flow: ownership and state guards, then charge and queue. Queued items complete through the normal production pipeline; poll GET /v2/viewcast/measurements/{asset_id} for completion.
  • final: the final measurement provider is stubbed upstream, so today every item reports failed:not_available and nothing is charged beyond the 1 credit base. When the provider is wired, each item will run the single final-purchase flow at the 300 credit rate.
  • direct: every item runs the single direct-purchase flow against the display catalog, with lookups fanned out to at most 10 concurrent requests. A display_id the catalog does not return reports skipped:not_in_catalog, and a display_id your organization already licenses reports skipped:already_licensed; both are decided before any charge. Queued items mint final_measured assets synchronously, and each starts its own 12-month subscription window.

What to read next


Did this page help you?