---
title: Captures
editUrl: https://github.com/Cratis/Screenplay/edit/main/Documentation/screenplay/captures.md
description: Captures convert external data into events — change data capture as a first-class construct. The body of a capture block is written in the Change Data…
---

Captures convert external data into events — change data capture as a first-class construct. The body of a `capture` block is written in the **Change Data Capture Language (CDL)** — an embedded sub-grammar the Screenplay parser delegates to (see [Sub-language Pluggability](/screenplay/sub-languages/)). Captures live inside `Translate` slices.

## Syntax

```screenplay
capture <Name>
  <CDL grammar>
```

## Example

```screenplay
capture LegacyInvoiceCapture
  source api
    api LegacyInvoicingApi
    route /invoices
    poll 5m
  key id
  map
    status = status translate
      "utkast"     => draft
      "sendt"      => sent
      "betalt"     => paid
      "kansellert" => cancelled
    split contactName by ","
      firstName
      lastName
  append InvoiceStatusChanged
    when status
      invoiceId = $.id
      status    = $.status
      changedAt = $context.occurred
  append InvoicePaidFromSent
    when status from "sent" to "paid"
      invoiceId = $.id
      paidAt    = $context.occurred
  children lineItems identified by lineNumber
    map
      productName = name
    append InvoiceLineItemAdded
      when added
        invoiceId  = $.id
        lineNumber = $.lineNumber
        quantity   = $.quantity
        unitPrice  = $.unitPrice
    append InvoiceLineItemRemoved
      when removed
        invoiceId  = $.id
        lineNumber = $.lineNumber
  nested billingContact
    map
      contactName = name
    append BillingContactUpdated
      when email
        invoiceId = $.id
        email     = $.email
```

## Capturing from public events

An inbound `Translate` slice turns the public events of another application into private events of its own. The capture reads them with `source events`, and the captured item is the incoming event's content:

```screenplay
slice Translate TrackShipments
  direction inbound
  event OrderShipped
    orderId String
  capture ShipmentTracking
    source events
      from ShipmentDispatched
      from ShipmentDelivered
    key orderId
    append OrderShipped
      when status from "pending" to "dispatched"
        orderId = $.orderId
```

Every `from` event must be a public event with an origin, an `append` event must be a private event of this application, and `source events` belongs only to an explicitly inbound slice (`PLAY0610`, `PLAY0612`, `PLAY0616`, `PLAY0619`, `PLAY0620`). The form selects ESM v9 and binds to an executable capture over events. The reference evaluator has no outside records to present to such a capture, so a `when capture` specification over events reports a typed Unsupported outcome instead of passing or failing. Rename follows a `from` line; see the [diagnostics](/screenplay/diagnostics/) for the full list.

## The CDL vocabulary at a glance

| Construct | Meaning |
| --- | --- |
| `source events` | Reads the public events of another application instead of an external system; each `from <Event>` line names one consumed event. |
| `source api` / `source webhook` / `source message` | Where the captured data comes from, with source-specific settings (`api`/`route`/`poll` for `api`, `path` for `webhook`, `topic` for `message`). |
| `key <property>` | Which source property identifies an instance. |
| `map` | Maps, splits and translates source values before events are appended. |
| `<property> = <source>` | Renames a source value onto a property, or assigns a `` `template` `` built from source values. |
| `translate` | Maps source values to Screenplay values (`"utkast" => draft`). |
| `split <source> by "<separator>"` | Splits one source value into several target properties. |
| `append <EventType>` | Appends an event when the source changes, guarded by a `when` trigger. |
| `tag <value>` | Attaches a [tag](/screenplay/events/#tags) to the appended event — a static identifier or string, or a `$context.` expression. |
| `when <property>` | Appends when the named property changes. |
| `when <property> or <property>` / `when <property> and <property>` | Appends when any (`or`) or all (`and`) of the named properties change. |
| `when <property> from <value> to <value>` | Appends when the named property transitions between two specific values. |
| `` when `<expression>` `` | Appends when a raw template-literal expression evaluates to true. |
| `when added` / `when removed` | Appends when an item appears in / disappears from the source. |
| `children <collection> identified by <key>` | Captures changes in a child collection, with its own optional `map`. |
| `nested <property>` | Captures changes to a single nullable child object, with its own optional `map`. |
| `$.<property>` | A value from the current source item. |
| `$context.occurred` | The capture timestamp. |

## In the executable semantic model

Captures bind to the executable semantic model as ESM v6 ([decision 0022](https://github.com/Cratis/Screenplay/blob/main/decisions/0022-esm-v6-time-triggers-captures-and-reactions-in-specifications.md)).
The reference evaluator is handed records - `given capture` and `when capture` in a [specification](/screenplay/specifications/#clocks-triggers-and-captures) -
and never contacts a `source`, which stays realization metadata. For each record:

- `map` operations apply in order, each to the record as the ones before it left it. A field `map` does not
  touch passes through unchanged.
- A condition compares the mapped record with the mapped record the capture last saw for the same `key`. A
  field the earlier record lacked has changed when the new one has it, so a first record has changed in every
  field it carries. `when status from "sent" to "paid"` compares mapped values.
- `` when `<expression>` `` holds a small portable grammar: fields, string, number, boolean and `null`
  literals, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`, `!` and parentheses. Ordering compares numbers only.
- `$.<field>` reads the item being evaluated - the record, a child or the nested record - and falls back to the
  enclosing record when the item lacks the field. `$context.occurred` is the scenario's `given clock`.
- Everything is appended to the event source the record's `key` names. Its type is the one the event's
  commands give it, and text when no command produces the event.
- The record's own appends run first, then each `children` collection's - current children in order, then the
  removed ones in their earlier order - then each `nested` record's.

The reference checks record shapes, event-source keys and child identities (including duplicates) before
appending anything. After those preconditions pass, it evaluates each reached append in order, enforces its
constraints, projects it and settles its reactions before evaluating the next capture effect. The first
rejection or Unsupported stops the scenario. Facts and projections from earlier accepted appends remain;
the failed append adds neither. A later unsupported mapping or guard cannot mask an earlier constraint
rejection. This is not a capture-wide transaction; invoked commands keep their own atomicity.

`given capture` supplies immutable last-seen input for one specification. Running the specification does not
store a new last-seen record or acknowledge an external source, on success or failure. Supply the baseline
explicitly in each subsequent specification. Retained prefix facts do not imply successful source processing
or acknowledgement. A present object or collection used as a scalar mapping is Unsupported, not a missing
value or a passing `then error`.

A capture without a `key`, or a condition outside the grammar above, does not bind (`PLAY0268`).

## Full CDL grammar

The authoritative CDL grammar - including the `split`, `nested` and richer `when` forms above - is published as the [Capture Declaration Language grammar](/screenplay/captures/grammar/), mirroring how the [Projection Declaration Language grammar](/screenplay/projections/grammar/) documents PDL.
