---
title: "Preview Rule"
method: POST
path: "/notifications/rules/preview"
tags: ["notification-rules"]
---

# Preview Rule

`POST /notifications/rules/preview`

## Request

- Base URL: `https://api-docs.upsmith.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api-docs.upsmith.com/notifications/rules/preview`
- Auth: one of:
  - API key in header `x-api-key`
  - HTTP bearer

Run a draft against real events without storing it.

Organization admin rather than member, although it stores nothing. It evaluates an arbitrary
caller-supplied document against this organization's real escalations and calls and reports
what it read, which is an authoring capability; a member who may read the rules list has not
thereby been given it.

Declared before ``/{rule_key}`` has no bearing -- there is no ``POST /{rule_key}`` -- but it
stays with the other literal paths so the file reads in one order.

## Headers

- `organization-id` string, nullable
- `x-user-id` string, nullable
- `timezone` string
- `x-actor-email` string, nullable
- `x-request-source` string, nullable
- `x-operation-id` string, nullable
- `x-commit-message` string, nullable
- `x-confirmation-id` string, nullable
- `x-chat-id` string, nullable

## Request body

- RulePreviewRequest: A draft, and one of the two ways to say which events to run it against. ``event_type`` previews against this organization's most recent real subjects of that type, rebuilt. ``events`` previews against events the caller supplies. Exactly one of them: neither leaves nothing to evaluate, and both would mean two answers to one question. ``limit`` and the length of ``events`` are bound by the same ``MAX_PREVIEW_EVENTS`` the library binds, declared here so the refusal names the field rather than arriving as a ``ValueError`` from two layers down.
  - `decision` object, required
  - `event_type` string, nullable
  - `events` RulePreviewEventInput[], nullable
    - `type` string, required
    - `occurred_at` string, date-time, required
    - `subject_id` string, required
    - `payload` object
  - `limit` integer

## Response `200`

Successful Response

- RulePreviewResponse: One preview run. ``event_source`` says which of the two ways the events were named, because it is what makes an empty ``unreadable`` readable: for ``supplied`` events there is nothing to be unreadable, while for ``recent`` ones an empty list means the rule read nothing the rebuild had lost.
  - `event_source` string, required
  - `previews` RulePreviewEntry[], required
    - `subject_id` string, required
    - `event_type` string, required
    - `applies` boolean, required
    - `outputs` RulePreviewOutputs, required: What the rule resolved to for one event. Empty is a result, not a failure.
      - `recipients` RecipientRef[]
        - `kind` 'user' | 'entity' | 'technician' | 'rotation', required: What a recipient reference names, and in whose id space. The three destinations ``notification.recipient.type`` has, restated so a rule document needs no ORM to be authored or validated, plus a rotation, which names a schedule rather than a row; ``tests/test_outputs.py`` holds the two lists together, as it does for the escalation reasons. Which id a reference carries follows from its kind, the way every notification settings route already reads one: ``user`` carries an ``auth.user.id``, ``entity`` and ``technician`` carry a ``notification.recipient.id``, neither having a user behind it, and ``rotation`` carries a rotation id (:func:`parse_rotation_id`). So every recipient a subscription names can be written as one of the first three, and the kind says what the reference can receive rather than hiding it behind a read. A reference is resolved to a person when the message is sent, never here -- which is what lets an escalation step reach whoever is on call at that moment rather than whoever was when the event happened.
        - `id` string, required
      - `channels` string[]
      - `timing` string, nullable
      - `escalation` EscalationStep[]
        - `delay_minutes` integer, required
        - `recipients` RecipientRef[], required
          - `kind` 'user' | 'entity' | 'technician' | 'rotation', required: What a recipient reference names, and in whose id space. The three destinations ``notification.recipient.type`` has, restated so a rule document needs no ORM to be authored or validated, plus a rotation, which names a schedule rather than a row; ``tests/test_outputs.py`` holds the two lists together, as it does for the escalation reasons. Which id a reference carries follows from its kind, the way every notification settings route already reads one: ``user`` carries an ``auth.user.id``, ``entity`` and ``technician`` carry a ``notification.recipient.id``, neither having a user behind it, and ``rotation`` carries a rotation id (:func:`parse_rotation_id`). So every recipient a subscription names can be written as one of the first three, and the kind says what the reference can receive rather than hiding it behind a read. A reference is resolved to a person when the message is sent, never here -- which is what lets an escalation step reach whoever is on call at that moment rather than whoever was when the event happened.
          - `id` string, required
      - `immediate_recipients` RecipientRef[]
        - `kind` 'user' | 'entity' | 'technician' | 'rotation', required: What a recipient reference names, and in whose id space. The three destinations ``notification.recipient.type`` has, restated so a rule document needs no ORM to be authored or validated, plus a rotation, which names a schedule rather than a row; ``tests/test_outputs.py`` holds the two lists together, as it does for the escalation reasons. Which id a reference carries follows from its kind, the way every notification settings route already reads one: ``user`` carries an ``auth.user.id``, ``entity`` and ``technician`` carry a ``notification.recipient.id``, neither having a user behind it, and ``rotation`` carries a rotation id (:func:`parse_rotation_id`). So every recipient a subscription names can be written as one of the first three, and the kind says what the reference can receive rather than hiding it behind a read. A reference is resolved to a person when the message is sent, never here -- which is what lets an escalation step reach whoever is on call at that moment rather than whoever was when the event happened.
        - `id` string, required
    - `rotations` RulePreviewRotation[]
      - `rotation_id` string, required
      - `output` string, required
      - `escalation_step` integer, nullable
      - `at` string, date-time, required
      - `name` string, nullable
      - `technicians` RulePreviewTechnician[]
        - `technician_id` integer, required
        - `name` string, required
        - `channels` string[]
        - `reasons` string[]
      - `members` RulePreviewMember[]
        - `user_id` string, required
        - `name` string, required
        - `channels` string[]
        - `reasons` string[]
      - `refusal` string, nullable
    - `blocked` string, nullable, required
    - `unreadable` string[], required
    - `inconclusive` boolean, required
    - `trace` object, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/upsmith/apis/upsmith-api.md) - [All operations](https://skmtc.dev/upsmith/apis/upsmith-api/llms.txt) - [OpenAPI document](https://skmtc.dev/upsmith/apis/upsmith-api/revisions/cce8b07a5787?raw)
