> For the complete documentation index, see [llms.txt](https://docs.agilecase.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.agilecase.com/developer-documentation/case-scripting/on-save-scripts.md).

# On Save Scripts

An on save script runs on the server as part of saving a case. Unlike an on load script it can change data: it can send email, create and complete tasks, update fields, add table rows and call webhooks. It can also refuse the save, which makes it the place to put validation that has to hold.

It runs on the server, so there is no page and no browser. There is no `document`, no jQuery and no `window`. Instead your script is handed a `Service` object that acts on the case for you.

## The shape of a script

Every on save script needs two things: a trigger on the first line, and a function called `on_save`.

```javascript
[Changed.CustomField("Customer Rating").Changed]
function on_save() {
    if (Changed.CustomField("Customer Rating").NewValueAsNumber < 0) {
        Service.AddMessage("A rating cannot be negative", "error");
        return false;
    }

    Service.AddTask("Review rating", "Rating changed, please review", Service.Case.FeeEarner, 7);
    return true;
}
```

### The trigger line

The first line declares which custom field the script watches:

```javascript
[Changed.CustomField("Customer Rating").Changed]
```

You can name the field or use its numeric ID, so `[Changed.CustomField(6364).Changed]` is equivalent. The name is matched loosely: spaces are ignored and case does not matter.

This line is not optional. A script with no trigger line, or one that does not match this pattern, is skipped and never runs. The trigger also gates execution: the script only runs when that field's value is actually different from what was stored before, so re-saving a case without touching the field does nothing.

{% hint style="info" %}
The trigger line is removed before your code is executed, so it does not need to be valid JavaScript. Everything after it does.
{% endhint %}

### The on\_save function

`on_save` must exist, and it must return `true` or `false`. If the function is missing, the script is skipped.

<table><thead><tr><th width="120">Return</th><th>What happens</th></tr></thead><tbody><tr><td><code>true</code></td><td>The save goes ahead. Everything the script asked <code>Service</code> to do is carried out.</td></tr><tr><td><code>false</code></td><td>The save is abandoned. The user's field changes are not written and nothing the script queued is carried out. Any message you added is still shown.</td></tr></tbody></table>

This is what makes validation possible: add a message explaining the problem, return `false`, and the user is told why their change did not stick.

### Running after the save instead

Name the function `after_save` rather than `on_save` and it runs after the field values have been committed, rather than before. Use this when the work only makes sense once the new values are stored, and where refusing the save is not the point, since by then there is nothing left to refuse.

A script can define both functions if it needs to do something in each phase. Each is called at its own point, and both need to return `true` or `false`.

{% hint style="info" %}
Anything the script sends out is better placed in `after_save`. Because `on_save` runs before the new values are written, an email or webhook queued there renders its merge fields against the values that were stored beforehand, so a template quoting the field that triggered the script would show the old value. Sending from `after_save` means the template sees what was actually saved.
{% endhint %}

## What your script can use

<table><thead><tr><th width="150">Object</th><th>What it is</th></tr></thead><tbody><tr><td><code>Service</code></td><td>Everything the script can do to the case, plus <code>Service.Case</code> for reading the case itself. See the <a href="/pages/FuuN9DD7yeJDbdU62JVM">Script API Reference</a>.</td></tr><tr><td><code>Changed</code></td><td>The custom fields being saved, with their old and new values. <code>Changed.CustomField("name")</code> returns one of them.</td></tr><tr><td><code>Convert</code></td><td>.NET type conversion helpers, such as <code>Convert.ToInt32</code>, for coercing field values.</td></tr></tbody></table>

`Changed.CustomField()` returns the same object as `Service.CustomField()`, so `NewValue`, `NewValueAsNumber`, `NewValueAsDate`, `OldValue` and `Changed` are all available on it. These are covered in full in the [Script API Reference](/developer-documentation/case-scripting/on-save-scripts/script-api-reference.md).

## Order of events

Nothing the script asks for happens as you call it. Calls to `Service` queue work, and the queue is only carried out once `on_save` has returned `true`. The order is fixed:

1. Custom field updates
2. Tasks added, then tasks completed
3. Emails, then SMS messages, then webhooks, then internal messages
4. Table rows added
5. Case information updates, such as status and milestones

A failure while sending an email or internal message, or while adding a table row, is reported to the user as an error message and the rest of the queue still runs. An unexpected error elsewhere stops the remaining work for that script.

{% hint style="warning" %}
Updating a custom field from a script that is itself triggered by a field change can cause the script to fire again. Guard against this by checking the current value before writing to it.
{% endhint %}

## When a save counts as a save

The script runs when a user saves the case, and also when a custom field group is saved on its own, so editing a single tab is enough to trigger it. It also runs when data arrives through an automated upload, which means a script can fire without anyone being at a screen to see its messages.

## Reference and examples

<table data-view="cards"><thead><tr><th>Page</th><th>What it covers</th><th data-card-target data-type="content-ref">Target</th></tr></thead><tbody><tr><td><strong>Script API Reference</strong></td><td>Every method on the <code>Service</code> object, with its arguments.</td><td><a href="/pages/FuuN9DD7yeJDbdU62JVM">/pages/FuuN9DD7yeJDbdU62JVM</a></td></tr><tr><td><strong>Dialog Formatting Options</strong></td><td>The notification and dialog styles available for messages.</td><td><a href="/pages/mhXbG3ksCbF75R4utltW">/pages/mhXbG3ksCbF75R4utltW</a></td></tr><tr><td><strong>Examples</strong></td><td>Fifteen worked scripts, indexed by what each one demonstrates.</td><td><a href="/pages/rpo2CdppzlrElWbAbklw">/pages/rpo2CdppzlrElWbAbklw</a></td></tr></tbody></table>

## Adding one

On save scripts are created in the same place as on load scripts. Choose the on save option when you set the script's type, as described in [Adding a New Script](/developer-documentation/case-scripting/adding-a-new-script.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.agilecase.com/developer-documentation/case-scripting/on-save-scripts.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
