# Require a Successful Listener Execution (https://www.shipfox.io/docs/how-to/author-workflows/define-listener-success)

Description: Fail a listening job unless it processes at least one event and every execution succeeds.

The default job result accepts a listener that resolves before it runs. Use a
custom `success` expression when an empty listener must fail.

This guide starts one run for a task, checks later status events, and finishes
when a final event arrives.

## Before you begin [#before-you-begin]

You need:

* A [custom webhook](https://www.shipfox.io/docs/integrations/webhooks) integration connection and its ingest URL.
* The integration connection slug. This guide uses `task_hook` as a placeholder.

## Add the workflow [#add-the-workflow]

Create `.shipfox/workflows/require-task-check.yml`. Replace `task_hook` with the
slug of the integration connection for the custom webhook. This is a complete workflow:

```yaml
# yaml-language-server: $schema=https://www.shipfox.io/docs/workflow.schema.json
name: Require a task check
runner: shipfox

triggers:
  task_started:
    source: task_hook # Replace with the slug of your webhook integration connection.
    filter: event.body.kind == "start"

jobs:
  remember:
    outputs:
      task_id: ${{ steps.save.outputs.task_id }}
    steps:
      - key: save
        env:
          TASK_ID: "${{ event.body.task_id }}"
        run: printf 'task_id=%s\n' "$TASK_ID" >> "$SHIPFOX_OUTPUT"
        outputs:
          task_id: string

  check:
    needs: remember
    success: 'executions.size() > 0 && executions.all(e, e.status == "succeeded")'
    listening:
      on:
        - source: task_hook # Replace with the same integration connection slug.
          filter: event.body.kind == "check" && event.body.task_id == jobs.remember.outputs.task_id
      until:
        - source: task_hook # Replace with the same integration connection slug.
          filter: event.body.kind == "finish" && event.body.task_id == jobs.remember.outputs.task_id
      timeout: 30m
      max_executions: 3
    steps:
      - env:
          CHECK_RESULT: "${{ execution.events[0].data.body.result }}"
        run: test "$CHECK_RESULT" = "pass"
```

The expression runs after the listener resolves. It requires a non-empty
`executions` list and rejects any failed execution. The exact execution fields
remain in the [Workflow schema](https://www.shipfox.io/docs/reference/workflow-schema#job-fields).

Commit and push the file to the project's default branch. Wait for **Require a
task check** to sync.

## Verify the result policy [#verify-the-result-policy]

Replace `<ingest-url>` below with the webhook URL. Use a new `task_id` for each
case.

1. **Successful case:** send `{"kind":"start","task_id":"task-pass"}`, then
   `{"kind":"check","task_id":"task-pass","result":"pass"}`, then
   `{"kind":"finish","task_id":"task-pass"}`. The `check` job succeeds.
2. **Failed case:** repeat with `task-fail` and a check result of `fail`. The
   execution fails, so the job fails after the finish event.
3. **Empty case:** send start and finish events for `task-empty` without a check
   event. The listener has no executions, so the job fails.
4. **Bound case:** send only a start event for `task-timeout`. After 30 minutes,
   the timeout resolves the empty listener and the job fails.

Send each body with this command:

```bash
curl -X POST '<ingest-url>' \
  -H 'Content-Type: application/json' \
  -d '<body-from-the-case>'
```

The [Contexts reference](https://www.shipfox.io/docs/reference/contexts#context-availability) lists the
predicate scope. The [Expressions reference](https://www.shipfox.io/docs/reference/expressions#functions-and-macros)
lists the syntax and functions.