> 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/script-api-reference.md).

# Script API Reference

On save scripts talk to AgileCase through a single object called `Service`. It is the only way a script can read or change anything, since the script runs on the server with no access to the page.

Most methods do not act immediately. They add to a queue that is carried out only if `on_save` returns `true`. The exceptions are the read methods (`Service.Case`, `Service.CustomField`, `Service.UserCode`, `Service.GroupCode`, `Service.DateDiff`, `Service.GetCurrentDate`) and `Service.AIAction`, which all return a value straight away.

{% hint style="info" %}
Anything the script asks for silently does nothing if its arguments are unusable, rather than raising an error. An `AddTask` with an empty description, or an `UpdateCustomField` with no field, is simply dropped. If a call appears to have no effect, check its arguments first.
{% endhint %}

## Reading the case

`Service.Case` is the case being saved. The properties most worth knowing:

<table><thead><tr><th width="220">Property</th><th>What it holds</th></tr></thead><tbody><tr><td><code>Service.Case.ID</code></td><td>The numeric case ID.</td></tr><tr><td><code>Service.Case.Number</code></td><td>The case reference shown to users.</td></tr><tr><td><code>Service.Case.Description</code></td><td>The case description.</td></tr><tr><td><code>Service.Case.ClientReference</code></td><td>The client's own reference for the case.</td></tr><tr><td><code>Service.Case.StatusText</code></td><td>The current status as text.</td></tr><tr><td><code>Service.Case.Labels</code></td><td>The case labels.</td></tr><tr><td><code>Service.Case.OpenDate</code></td><td>When the case was opened. <code>CloseDate</code> and <code>PendingDate</code> are also available.</td></tr><tr><td><code>Service.Case.FeeEarner</code></td><td>The fee earner, as a user. Pass this straight into <code>AddTask</code>.</td></tr><tr><td><code>Service.Case.Partner</code></td><td>The partner, as a user.</td></tr><tr><td><code>Service.Case.ClientContact</code></td><td>The client, as a contact.</td></tr><tr><td><code>Service.Case.Source</code></td><td>The source, as a contact.</td></tr><tr><td><code>Service.Case.CaseType</code></td><td>The case type, whose <code>Name</code> is often useful in a shared script.</td></tr></tbody></table>

## Reading custom fields

```javascript
Service.CustomField(name)
Service.CustomField(id)
```

Returns a field value object. The name is matched with spaces removed and case ignored, so `"Customer Rating"`, `"customerrating"` and the numeric ID all find the same field. Fields that exist but have never been given a value come back with their configured default.

`Changed.CustomField()` returns the same kind of object, for fields included in the save currently in progress.

<table><thead><tr><th width="220">Property</th><th>What it holds</th></tr></thead><tbody><tr><td><code>Id</code>, <code>Name</code></td><td>The field's ID and name.</td></tr><tr><td><code>Changed</code></td><td><code>true</code> when the old and new values differ.</td></tr><tr><td><code>Value</code>, <code>ValueAsText</code></td><td>The value as displayed.</td></tr><tr><td><code>ValueAsNumber</code></td><td>The value as a whole number, or <code>0</code> if it is not numeric.</td></tr><tr><td><code>ValueAsDate</code></td><td>The value as a date.</td></tr><tr><td><code>OldValue</code>, <code>OldValueAsText</code>, <code>OldValueAsNumber</code>, <code>OldValueAsDate</code></td><td>The value before this save.</td></tr><tr><td><code>NewValue</code>, <code>NewValueAsText</code>, <code>NewValueAsNumber</code>, <code>NewValueAsDate</code></td><td>The value being saved.</td></tr></tbody></table>

{% hint style="warning" %}
`ValueAsNumber` uses whole numbers only, so a field holding `12.5` reads as `0`, not `12`. For decimals, read `ValueAsText` and convert it yourself with `parseFloat`.
{% endhint %}

## Messages to the user

```javascript
Service.AddMessage(message, type, title, reload)
```

Only `message` is required; `type` defaults to `"info"`. Messages are shown whether the script returns `true` or `false`, which is how you explain a refused save.

The `type` decides how the message appears. A plain type produces a dialog the user has to dismiss. Prefixing it with `notify-` produces a notification in the top right that fades after about five seconds.

<table><thead><tr><th width="200">Type</th><th>Appearance</th></tr></thead><tbody><tr><td><code>info</code>, <code>success</code>, <code>warn</code>, <code>error</code></td><td>A dialog box, coloured to match. <code>title</code> sets its heading, defaulting to "Message".</td></tr><tr><td><code>notify-info</code>, <code>notify-success</code>, <code>notify-warning</code>, <code>notify-error</code></td><td>A notification that appears briefly and disappears on its own.</td></tr></tbody></table>

If a script adds more than one dialog message they are stacked into a single dialog. The `title` is only used when there is exactly one.

The fourth argument, `reload`, is accepted but has no effect on the case page.

## Tasks

```javascript
Service.AddTask(description, notes, assignedTo, dueDate)
Service.AddTask(description, notes, assignedTo, dueDate, taskListShortGuid)
```

Both `description` and `notes` are required; the task is dropped if either is empty.

`assignedTo` takes either a single user, such as `Service.Case.FeeEarner`, or a list of assignment codes built with `Service.Assignments`, which is how you assign to several people or to a group.

`dueDate` takes either form:

<table><thead><tr><th width="200">Form</th><th>Meaning</th></tr></thead><tbody><tr><td>A number, such as <code>7</code></td><td>Due that many days from now. Negative numbers are rejected and the task is dropped.</td></tr><tr><td>A string as <code>yyyyMMdd</code>, such as <code>"20260401"</code></td><td>Due on that exact date. Any other format is rejected and the task is dropped.</td></tr></tbody></table>

`taskListShortGuid` optionally places the task on a particular task list. Without it the task is added to the case on its own.

```javascript
Service.AddTask("Review rating", "Rating changed, please review",
                Service.Assignments(Service.UserCode("Jane Smith"),
                                    Service.GroupCode("Underwriting")),
                7, taskListGuid);
```

### Completing a task

```javascript
Service.CompleteTask(description, taskListShortGuid)
```

Completes the matching task on that task list. Use it to close off a step once a field confirms the work is done.

## Assignment helpers

<table><thead><tr><th width="260">Method</th><th>Returns</th></tr></thead><tbody><tr><td><code>Service.UserCode(name)</code> or <code>Service.UserCode(id)</code></td><td>An assignment code for that user, or an empty string if there is no match. The name is the user's display name.</td></tr><tr><td><code>Service.GroupCode(name)</code> or <code>Service.GroupCode(id)</code></td><td>An assignment code for that group, or an empty string if there is no match.</td></tr><tr><td><code>Service.Assignments(...)</code></td><td>Combines any number of codes into a list for <code>AddTask</code>. Empty strings are discarded, so an unmatched name is skipped rather than breaking the call.</td></tr></tbody></table>

Because an unmatched name yields an empty string, a misspelled user simply drops out of the assignment. It is worth checking the code is not empty before relying on it.

## Changing data

### Custom fields

```javascript
Service.UpdateCustomField(id, newValue)
```

Sets a custom field to a new value. The field can be given by ID or by name.

### Case information

```javascript
Service.UpdateCaseInfo(fieldName, newValue)
```

Two kinds of `fieldName` are recognised.

<table><thead><tr><th width="240">Field name</th><th>Effect</th></tr></thead><tbody><tr><td><code>"status"</code></td><td>Sets the case status. <code>"Open"</code>, <code>"Pending"</code> and <code>"Closed"</code> are recognised, in any capitalisation. Closing a case also stamps its close date, and reopening one clears it. An empty value resets the case to Open.</td></tr><tr><td><code>"milestone.&#x3C;name or id>"</code></td><td>Sets the date of that milestone on the case, for example <code>"milestone.FileReview"</code>. The value must be a date, or empty to clear it.</td></tr></tbody></table>

{% hint style="warning" %}
Any other status value is stored as free text. The case then displays that word as its status but is not treated as open, pending or closed by the rest of the system, so it will not behave as a closed case in reports or filters. Spelling matters here: `"closed"` closes a case, `"close"` does not.
{% endhint %}

A milestone that does not exist on the case, or a value that is not a date, produces an error message rather than a silent failure.

### Table rows

```javascript
Service.AddTableRow(customFieldGroupId, values)
```

Adds a row to a custom field table. `customFieldGroupId` is the numeric ID of the table's custom field group. `values` is a JSON string holding an array of field IDs and their values:

```javascript
Service.AddTableRow(412, '[{"ID":6364,"Value":"75"},{"ID":6365,"Value":"Reviewed"}]');
```

## Sending things out

Each of these names a template that must already exist in your settings. All four queue the send until the script returns `true`.

<table><thead><tr><th width="290">Method</th><th>What it sends</th></tr></thead><tbody><tr><td><code>Service.SendEmail(templateName)</code></td><td>The named email template, with the case's merge fields filled in. An optional second argument passes input into the template.</td></tr><tr><td><code>Service.SendSMS(templateName)</code></td><td>The named SMS template.</td></tr><tr><td><code>Service.SendWebhook(templateName)</code></td><td>An HTTP request to another system, configured on an SMS template. See <a href="/pages/OYeS6gu3dBbjNF38tQXv">Outgoing Webhooks</a>.</td></tr><tr><td><code>Service.SendMessage(templateName)</code></td><td>An internal message to a case contact, from an email template labelled as a message.</td></tr></tbody></table>

If a send fails, the failure becomes an error message on the case and the rest of the queue still runs.

## AI actions

```javascript
Service.AIAction(templateName, input, documentId, type, extraPromptText)
```

Runs a configured AI template and returns its result as a string, so unlike the send methods this happens immediately and you can act on the answer inside the same script. Only `templateName` is required. Pass `input` to give the template text to work on, or `documentId` to point it at a document on the case. `extraPromptText` is appended to the template's own prompt.

## Dates

<table><thead><tr><th width="290">Method</th><th>Returns</th></tr></thead><tbody><tr><td><code>Service.GetCurrentDate()</code></td><td>Today's date as <code>yyyy-MM-dd</code>.</td></tr><tr><td><code>Service.DateDiff(startDate, endDate)</code></td><td>The number of whole days from the first date to the second. Negative if the second is earlier.</td></tr></tbody></table>

`DateDiff` accepts dates written with slashes, hyphens or spaces, in either day-first or year-first order. Slash and hyphen formats are read day-first, so `01/02/2026` is 1 February. To avoid the ambiguity entirely, use `yyyy-MM-dd`, which is also what `GetCurrentDate` returns.

Unlike most of the API, `DateDiff` throws if a date cannot be read at all. An unhandled error stops the script and shows the user an error message, so check your input if the dates come from a field a user can type into.

```javascript
var age = Math.floor(Service.DateDiff(Service.CustomField("Date of Birth").ValueAsText,
                                      Service.GetCurrentDate()) / 365.25);
```


---

# 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/script-api-reference.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.
