---
id: scripting-services
title: "Flow services"
summary: "Everything Services offers to scripts - logging, designer messages, settings, storage and sequences, assets, batches, alarms, email, starting flows, and flow and component information. Same names in C#, Python and JavaScript."
audience: user
keywords: "services, IFlowService, Log, Messaging, Settings, Storage, Memory, Shared, Disk, Sequences, Assets, Batches, Alarms, Email, Activator, HandleExceptionAsync"
---
# Flow services

Every script gets the flow services: `Services` in C#, `services` in Python and JavaScript. The members have the same
names in all three languages, so an example in one language reads the same in the others.

| Member | What it gives you |
|---|---|
| `Log` | Write to the flow log |
| `Messaging` | Show messages and progress in the designer |
| `Settings` | Read expression settings |
| `Storage` | Keep values in memory or on disk, and number sequences |
| `Assets` | Read files that come with the flow |
| `Batches` | Group records into a batch |
| `Alarms` | Raise and clear alarms |
| `Email` | Send email through the environment's SMTP server |
| `Activator` | Start another flow |
| `Flow`, `Component` | Information about the running flow and component |
| `CancellationToken` | Signalled when the flow stops |
| `HandleExceptionAsync` | Report an error and carry on |

## Log

Writes to the flow log, which you search in the **Logs** viewer.

```csharp
Services.Log.Information("Order imported");
Services.Log.Warning("Customer has no email", metaData, input);
Services.Log.Error("Payment failed", ex, metaData, input);
```

Levels: `Verbose`, `Debug`, `Information`, `Warning`, `Error`, `Fatal`. Each takes a message and, optionally, the
metadata and the record, so the log entry links to the message and its data. `Error` also takes an exception. The
flow's **Log level** decides which levels are written.

## Messaging

Shows messages on the designer, for the people watching the flow.

| Method | What it does |
|---|---|
| `Information`, `Warning`, `Error`, `Trace` | A line in the trace console |
| `UpdateStatusMessage(text)` | Status text on the component |
| `UpdateProgress(value)` | A progress bar on the component, from 0 to 1 |
| `StopProgress()` | Removes the progress bar |
| `Clear()` | Clears the component's messages |

## Settings

Reads a value from an expression settings package. Values are cached for 30 seconds, so a changed setting reaches
running flows quickly without a restart.

```csharp
string url = Services.Settings.Get("Crm", "ApiUrl");
int limit = Services.Settings.Get<int>("Crm", "PageSize");   // C# only
```

In Python and JavaScript, `Get` returns text; convert it yourself (`int(...)`, `Number(...)`).

## Storage

| Member | Scope | Keeps |
|---|---|---|
| `Storage.Memory` | This flow | Any object, until the flow stops |
| `Storage.Shared` | All flows on the host | Any object, until the host stops |
| `Storage.Disk` | This flow | Bytes, across restarts |
| `Storage.Sequences` | All flows, by name | Number sequences, across restarts |

Memory and Shared have `TryGetValue`, `Add`, `TryAdd`, `AddOrUpdate`, `Get` and `Delete`. Disk has `Get`,
`AddOrUpdate` and `Delete`.

```csharp
if (!Services.Storage.Memory.TryGetValue<string>("token", out var token))
{
    token = await GetTokenAsync();
    Services.Storage.Memory.AddOrUpdate("token", token);
}
```

Sequences give unique, increasing numbers that survive restarts:

```csharp
Services.Storage.Sequences.NewIfMissing("invoice");
long number = Services.Storage.Sequences.Next("invoice");
```

`New(name, start)` and `Reseed(name, start)` set the start value; `Delete(name)` removes the sequence.

`Storage.GetLocalDataPath()` returns a folder for the flow's own files, and `Storage.GetDatabasePath(name)` a path for
a local database file.

## Assets

Files that come with the flow, such as templates or lookup tables.

```csharp
if (Services.Assets.HasAsset("countries.json"))
{
    var countries = Services.Assets.GetContent("countries.json").Cast<string>();
}
```

## Batches

A batch groups the records produced from one unit of work, such as one file or one query result. Components that work
in batches, such as a bulk insert or a file writer, commit when the batch completes and roll back when it fails.

Open a batch, send its records with `batch.MetaData`, and complete it. Leaving without completing fails the batch.

```csharp
await using var batch = Services.Batches.Begin(metaData, input.FileName);
foreach (var line in input.Lines)
    await NextAsync(batch.MetaData, new Row { Text = line });
await batch.CompleteAsync();
```

```python
with services.Batches.Begin(metaData, inputData.FileName) as batch:
    for line in inputData.Lines:
        row = Row()
        row.Text = line
        services.Next(batch.MetaData, row)
    batch.Complete()
```

In JavaScript, call `batch.Complete()` in the `try` block and `batch.Dispose()` in `finally`. `batch.Fail()` ends a
batch as failed on purpose.

## Alarms

Raise an alarm defined in the environment's alarm definitions. It is delivered by email, web push or SNMP trap, as
the definition says.

```csharp
string alarmId = Services.Alarms.Raise("PaymentGatewayDown", "No answer for 60 s", "Payments");
Services.Alarms.Clear("PaymentGatewayDown", alarmId, "Payments");
```

## Email

`Services.Email.GetDefaultSmtpClient()` returns an SMTP client configured for the environment, and
`GetDefaultSender()` the default sender address.

## Starting flows

`await Services.Activator.Start(flowId)` starts another flow by its id.

## Flow and component

`Services.Flow` has the flow's `Id`, `Name`, `NameSpace` and configuration; `Services.Component` the component's `Id`
and `Name`.

## Errors

`HandleExceptionAsync(metaData, inputData, ex)` hands a caught exception to the flow's error handling, then your code
carries on. Use it in a loop where one bad item shouldn't stop the rest.

```csharp
foreach (var item in input.Items)
{
    try { await NextAsync(metaData, Map(item)); }
    catch (Exception ex) { await Services.HandleExceptionAsync(metaData, input, ex); }
}
```
