> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cronho.st/llms.txt
> Use this file to discover all available pages before exploring further.

# Execution Model

> How Cronhost decides success and failure, when it retries, and how executions count toward your plan

# Execution Model

This page explains what happens each time one of your schedules fires: how the
outcome is decided, when Cronhost retries, and how executions count toward your
plan.

## Success and failure

By default, a job is a **success when the response status is below 400** (any
2xx or 3xx). Any status of 400 or above is a **failure**, and so are network
errors and timeouts.

You can override this per schedule with `expectedStatusCodes`. It is a
comma-separated list of 3-digit status codes and ranges. When set, only those
codes count as success.

```
200-299,410
```

* Omit the field or leave it blank to use the default rule (`< 400`).
* Whitespace is ignored, so `200-299, 410` and `200-299,410` behave the same.

A common use case: an endpoint that returns `202 Accepted` and later `409
Conflict` on duplicate work, where you consider both healthy. Set
`expectedStatusCodes` to `200-299,409`.

<Note>
  `fetch` follows redirects, so a final 3xx response is uncommon in practice.
  Most schedules only need to think about 2xx versus 4xx/5xx.
</Note>

## Retries

When a job fails, Cronhost retries only when the failure looks **transient**:

* `5xx` server responses
* `408 Request Timeout`
* `429 Too Many Requests`
* Connection or network errors
* Request timeouts

Deterministic `4xx` responses (such as `400`, `401`, `403`, `404`) are **not
retried**, because repeating the same request would just fail again.

Retries use exponential backoff. Each retry is recorded as its own job row with
an incremented `attemptNumber`, sharing the original scheduled run time.

### maxRetries

`maxRetries` is the **total number of attempts including the first**, not the
number of extra tries. It is clamped to the range **1 to 10**: a value of `0` is
treated as `1`, and values above `10` are capped. Out-of-range values are never
rejected, so existing integrations keep working.

### timeoutSeconds

`timeoutSeconds` is the per-request timeout, clamped to the range **1 to 300**.

## How executions count toward your plan

Your plan includes a monthly execution allowance (see [Plans & Limits](/plans)).
An **execution** is a scheduled run or a manual trigger, counted once per
scheduled run time.

**Retries are free.** They are the platform's reliability mechanism and do not
consume your quota, so a flaky endpoint that needs several attempts still costs
a single execution.

## Notifications

Each schedule has a notification preference (`notifyOn`) that decides which
outcomes alert you: `none`, `failure`, `success`, or `both`. Alerts are
delivered to verified notification channels (email, Slack, Discord, or
Telegram).

See [Notifications](/sdk-reference/notifications) for how to set this up with
the SDK, or the API reference for the raw endpoints.
