# Docs

## Quickstart

## Accessing Tines

To sign into Tines you need a tenant. A tenant is created when you purchase a Tines [plan](/pricing) or when you sign up for our forever-free [Community Edition](/blog/announcing-the-tines-community-edition).

## Resources

Tines offers a few different formats for documentation and support depending on what you're looking to achieve:

**How to:**

- [Explained](https://explained.tines.com/en/): Quick advice and answers from our team around best practices, complex building techniques, authentication guides, and more.
- [Docs](https://www.tines.com/docs/quickstart/): What you're viewing now — technical overviews of different product features.
- [API Docs](https://www.tines.com/api/welcome): Technical documentation for the API accessible parts of our product.

**Getting started:**

- [Library](https://www.tines.com/library): Pre-built end-to-end workflows that offer inspiration for your next build. You can also use them as a starting point and customize within your own tenant for quick time to value.
- [Community Slack](https://hq.tines.io/pages/6f8b122ccba3cb7e8e0d3531d1b70eb2/): Form to join our community Slack to discuss and troubleshoot automation ideas with other Tines users.

**Deeper contextual learning:**

- [Bootcamp](https://www.tines.com/bootcamp-series): Step-by-step guide to building a sample story. Offered in live session, user-led, and self-guided formats.
- [University](https://www.tines.com/university): Dedicated learning paths for beginner, intermediate, and advanced users.
- [Certification](https://www.tines.com/get-certified): Tines certification after completing and passing the course.



We regularly share [blog](https://www.tines.com/blog) posts highlighting best practices and use cases of Tines with other vendors.

### Signing in

Each Tines tenant has a unique tenant domain, which is similar to either 
`adjective-noun-1234.tines.io`  or  `adjective-noun-1234.tines.com`. Tenants may also be named after your organization: `your-company.tines.com`.

If you know your tenant domain, you can enter it into your browser's address bar to sign in. Alternatively, you can click the "Log in" button at [tines.com](https://tines.com/) from a browser that you previously used to sign in to your tenant. 

> **NOTE:** If you do not know or have forgotten your tenant domain, contact [support@tines.io](mailto:support@tines.io) for assistance.

We support signing in with Google, Microsoft, or via your email address using a one-time link. You can also configure your tenant to use single sign-on via SAML or OIDC. 

Visit our [admin docs](https://www.tines.com/docs/admin/user-administration/) for for more information about authentication, sessions, [SSO](https://www.tines.com/docs/admin/single-sign-on/) and other forms of [user administration](https://www.tines.com/docs/admin/user-administration/).

## Stories

## Introduction

Stories are a collection of interconnected actions working towards a singular mission that can be automated. They can be viewed as analogous to playbooks, use-cases, or workflows. To support sharing, stories can be imported and exported. 

A story is made up primarily of [actions](https://www.tines.com/docs/actions) and [tools](https://www.tines.com/docs/actions/tools/). 

> **TIP:** Get inspiration for your next story with our [library](https://www.tines.com/library/) of pre-built examples of Tines in action.

## Adding actions to stories

[Actions](https://www.tines.com/docs/actions) can be added to a story in three ways:

1. Dragging an empty action or a template onto the storyboard.
2. Copy and pasting actions into the storyboard from another story. 
3. Copy and pasting a cURL request into the Tines storyboard. 

Learn more about actions [here](https://www.tines.com/docs/actions/).

## Default stories

When Tines tenants are created they contain 1 story:

**Your first story**: This story contains a quick video demonstrating the basics of Tines and instructions to build your first story.

### Story toolbar

# Overview

The story toolbar is a floating toolbar with a consistent structure where all actions are surfaced naturally. By default you'll see the most used actions and you'll be able to modify it to your own preferences by drag and dropping a actions onto the toolbar. Each toolbar is customised per user per tenant.

#### QuickSearch

The quick search can be found by clicking the quick search button at the top of the toolbar or using the keyboard shortcut `A`.

![](https://www.datocms-assets.com/55802/1777303268-screenshot-2026-04-27-at-16-20-53.png)

#### Templates

The templates can be found by clicking on the HTTP Request action or using the keyboard shortcut `T`.

![](https://www.datocms-assets.com/55802/1777303429-screenshot-2026-04-27-at-16-23-26.png)

#### Adding & Removing actions

When inside of the quicksearch or templates popups, simply drag and drop the action into the toolbar.

**

### Story runs

## Overview

Stories consist of [actions](https://www.tines.com/docs/actions), and [events](https://www.tines.com/docs/events) are created as a result of actions running. The chain of actions and events set off by a run is called a story run. Story runs can consist of unlimited amounts of actions and are recorded with a unique GUID (globally unique identifier) for each chain of events that occurs. The unique identifier of the story run can be obtained from [the `META` formula key](https://www.tines.com/docs/formulas/referencing-data#metadata), or the  [`STORY_RUN_GUID` function](https://www.tines.com/docs/formulas/functions/story-run-guid). Note: In the case of Send to Story, obtaining the story run guid using the `STORY_RUN_GUID` function will yield the calling story's story run guid in both the calling story AND the sub-story, so if you need the sub-story's story run guid, use the `META` formula key (i.e., `META.story_run.id`).

When reviewing events from a story, the story runs view can be utilized to present a clear picture of events as they took place during an execution route.

### Storyboard view

Select **"Show this run's events only"** from the kebab menu within any event in the event viewer to filter the storyboard to that event's story run. This makes it easier to visually trace or debug a particular run through a busy or complicated story.

**

Select **"Show this event's path only"** from the kebab menu within any event to filter the storyboard to that event's path through the story run. This is a more granular companion to "Show this run's events only" and is particularly useful with an Event Transform action in Explode mode, where a single event can fan out into many events, each taking its own path. The filtered view won't always be a single linear path; if the event encounters branching logic or further Explode actions along its route, those will also be included. 

### Story run view

The story run view can be accessed by selecting "Story runs" located within the kebab menu at the top right of your storyboard.

**

### Event routes

Within the story runs tab, there is a story runs panel and a routes panel. The story runs panel groups the latest set of actions and their corresponding events together by story run. Each run is listed in order by most recent runtime along with the number of actions in that story run.

Clicking on an individual story run in the story runs panel will display all actions that took place during that story run. These actions will appear in the routes panel. At the bottom of the routes panel, the following story run metrics are reported:

- The number of events that took place across the number of actions in the story run
- Story run start time
- Story run finish time
- Story run duration

Clicking on an action within the routes panel will display the corresponding event in the panel to the right along with all preceding and/or succeeding events. This provides a single view of the details of all events in a route. The arrow beside Routes also links out to the filtered storyboard view of the story run (i.e. the storyboard with "[Show this run's events only](https://www.tines.com/docs/stories/story-runs/#storyboard-view)" applied). 

Events can take multiple routes during a single story run, usually as a result of "exploding" an array or branching to multiple actions in a story. When an individual action is selected in the routes pane, the actions that were part of that particular route will remain highlighted, making it easier to understand which actions took place to arrive at a particular event. 

**Note:** You can also visualize this easily on the storyboard by using "[Show this event's path only](https://www.tines.com/docs/stories/story-runs/#storyboard-view)."

![](https://www.datocms-assets.com/55802/1705442335-routes-tab.jpg)

### View and edit actions from story runs

Clicking on the action title of any of the events seen in the primary event viewer will populate the selected action's editor panel. This panel allows for viewing and configuring actions in real time. This is helpful when editing action configurations while referencing a wide range of event data from previous story runs.

![](https://www.datocms-assets.com/55802/1705441561-action-editor.png)

### View story runs from the event panel

Within the events viewer, you can select the kebab menu and click "Open story run" to open an event in the context of its story run. This allows you to more easily understand what happened before or after an event occurred.

**

Looking for **Saved story runs**, find them [here](https://www.tines.com/docs/stories/testing/).

### Story versions

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

## Overview

Story versions allows teams to back up, inspect, clone, export, and restore any workflow. Think of it as a time machine for your company’s mission-critical processes. Access Version History by clicking on the down arrow next to your story name in the top middle section of the storyboard and selecting 'Versions'. 

![](https://www.datocms-assets.com/55802/1776422801-story-versions-highlighted-in-dropdown.png)

A version is auto-generated if a story is not further edited for at least 5 mins. For a more granular snapshot, a version can be manually generated by clicking the plus icon.

## Version actions

Once a version is created, different actions can be taken:

- **Rename**: Give the selected version a more memorable name
- **Clone**: Clone the selected version to a new story
- **Export**: Export the selected version as a JSON file
- **Delete**: Delete the selected version

![](https://www.datocms-assets.com/55802/1724324214-versions-actions.png)

## Preview

Preview allows for review of the overall story and inspection of specific actions. This can be helpful if you wish to review or recreate a specific action's configuration after an edit. Click the preview icon on the version you wish to preview. This will generate a preview of that selected story version and highlight specific changes that occurred. Click Restore to restore the main story to the specified version. Additionally, you can click the arrows to preview other story versions.

**

### Importing and exporting

## Overview

To support sharing, stories can be imported and exported. Story files contain all actions and their sources/receivers. Story files do not contain any event data.

## Importing Stories

To import a Tines story file, from the Stories page, select "Import": 

![](https://www.datocms-assets.com/55802/1745924429-screenshot-2025-04-29-at-11-39-23.png)

Either drag and drop a story file into this window or select the file from your computer. 

![](https://www.datocms-assets.com/55802/1745924914-screenshot-2025-04-29-at-12-08-19.png)

If a story file contains a story with a  name that matches an existing story name in the current team, an additional import option will be shown.

"Replace" will replace the story with the matching name with the imported story.

![](https://www.datocms-assets.com/55802/1711017535-screenshot-2024-03-21-at-10-32-35-am.png)

## Exporting Stories

To export a story, with no actions selected, click on the button in the top right and select "Export story". 

![](https://www.datocms-assets.com/55802/1685441919-screenshot-2023-05-30-at-11-18-22.png)

After clicking on the export option a story file will be downloaded.

### Keep Events and Logs (Event Retention)

## Overview

Keep events and logs (also referred to as the "event retention setting") is a configurable time period that determines how long Tines retains data associated with a story before it is automatically deleted. This setting helps manage storage and ensure compliance with data retention policies.

## What It Controls

The event retention setting controls the retention period for the following types of data:

1. Events - All events created by actions in the story, including events queued for Throttle or Delay actions.
2. Action Logs - Log entries generated by actions during story execution.
3. Story Runs - Execution records of story runs (unsaved runs only).

This does **not** include [audit logs](https://www.tines.com/docs/admin/audit-logs/).

Once the retention period expires, this data is automatically deleted by scheduled cleanup jobs. The retention period is calculated from the creation time of each record.

## How It Works

- Data is retained for the specified duration from its creation time.
- After the retention period expires, data is automatically deleted by background cleanup processes.
- The setting applies to all event, action log, and story run data associated with the story, regardless of which action created it.
- Changes to the retention period update the retention period for all records on that story that have already been created. This behavior is subject to change. 

## Groups

For [groups](https://www.tines.com/docs/stories/groups/), the event retention setting is inherited from the parent story (the story containing the Group action). Groups cannot have their own independent retention settings - they always use the parent story's setting. This ensures consistent data retention across all stories in a group hierarchy.

## Available Values

The following retention periods are available (depending on your plan):

- 1 hour
- 6 hours
- 1 day
- 3 days
- 7 days (default)
- 14 days
- 30 days
- 60 days
- 90 days
- 180 days
- 365 days

## Important Notes

- Default retention: If not specified, stories default to 7 days.
- Saved story runs: Saved story runs are not affected by the retention setting and are retained indefinitely.
- Data deletion: Deletion happens automatically via scheduled background jobs - you don't need to manually delete expired data.

## Example

If you set your event retention to 1 day on a story:

- An event created in that story on January 1st at 10:00 AM will be deleted on January 2nd at 10:00 AM
- All action logs in that story created on January 1st will be deleted after 24 hours
- Unsaved story runs in that story from January 1st will be deleted after 24 hours

### Send to Story

## Overview

Teams regularly need to perform the same task or a set of tasks across different stories. For example, a threat intelligence story and a phishing response story may use the same procedure to analyze a URL. Similarly, a user deprovisioning story and a vulnerability management story may both require the creation of a Jira ticket.

Rather than recreating the same set of actions across different stories (thus violating the [DRY-principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)), ***Send to Story*** allows users create reusable "sub-stories" to which events can be sent from other stories. When an event is received by a sub-story, the sub-story will perform its action and when finished, emit an event from the calling action.

> **IMPORTANT:** Sub-stories need to be [published](https://www.tines.com/docs/stories#publish-unpublish-stories) to receive events from calling actions.

## Sub-stories

Sub-stories work exactly the same as normal Tines stories. The key difference is that a sub-story has an input action and an output action. The input action must be a webhook type action and the output action must be a message-only mode event transform action.

### Enabling a story for Send to Story (creating a sub-story)

In the storyboard's properties panel (visible when no actions are selected), toggle the Send to Story option to enable it. A modal will appear to specify input and output actions. A Send to Story can only have one input action, but it can have many output actions. Once an input action is defined, you can specify input parameters for your Send to Story: the required inputs needed for the story to run.

> **WARNING:** When enabling a story for Send to Story, remember that story names should be unique per team and there **cannot be multiple Send to Stories with the same name in one team**.

![](https://www.datocms-assets.com/55802/1776423105-screenshot-2026-04-17-at-11-51-10.png)

### Input

When an event is sent to a Send to Story, the input action will emit an event to its receiver actions. Input actions must be of type Webhook.

### Send to Story inputs

Inputs are the defined values your sub-story needs to run. These will be auto-populated in the [Send to Story action](https://www.tines.com/docs/actions/types/send-to-story) when your sub-story is specified. These inputs can also be updated on the webhook action itself.

> **WARNING:** If no inputs are defined, the Send to Story action will default to a payload object. This default payload object is ignored once any inputs are defined

![](https://www.datocms-assets.com/55802/1719337265-sts-inputs.png)

### Updates to Send to Story inputs

You can update Send to Story inputs from the configuration panel or the webhook editor at any time. Once updated, any send to story actions calling your sub-story will reflect these changes visually, but their editor configuration (`<Editor>` button on the action) will not change.

If you do not have any send-to-story inputs defined, the payload of the Send to Story action will be received by the input action. If you do have Send to Story inputs defined, regardless of the editor configuration defined on the Send to Story action, only the defined Send to Story inputs of your sub-story will be received by the input action. If you have a payload defined for a Send to Story action that doesn't have Send to Story inputs defined, and you add Send to Story inputs to the input action, the payload will be ignored. The payload will still be visible on the action configuration but it will be marked "ignored".

![](https://www.datocms-assets.com/55802/1722507724-screenshot-2024-08-01-at-11-21-54.png)

A Send to Story action that does not send all required inputs will fail and log an error that the call is missing required inputs.

### Output

The output actions are the last action in a Send to Story and must be message-only mode event transform actions. The content specified in the Exit action will be emitted by the Send to Story action that originally sent the event to the sub-story.

### Access

Controls who is allowed to send to this story. Either select 'All teams' to allow any team to send to this story, select the specific teams that should have acces and/or select current team's members' personal teams to share with all members of the current team that have story run permissions. Users must have story manage permissions in the current team to share the story.

You can view all Send to Stories available to your team by clicking the "Shared with this team" section in the stories page. You will not be able to view or modify the contents of the story unless you have the relevant permissions in the team owning said story.

> **NOTE:** Need to share with other teams? [Reach out to the team](https://www.tines.com/contact-support) to discuss adding this feature to your Tines tenant.

### Timeout settings

When invoked as a tool from [**Workbench**](https://www.tines.com/docs/workbench/) or an** **[**AI Agent**](https://www.tines.com/docs/ai-agent/), a story with this setting enabled will gracefully conclude after the specified timeout duration has elapsed if the story did not complete in time.

### Enable for

Controls where this send to story can be used:

- **Send to Story**: Story can be called via the Send to Story action
- [**Workbench**](https://www.tines.com/docs/workbench/)** and Send to Story**: Story can be called via the Send to Story action and Workbench.
- [**Workbench**](https://www.tines.com/docs/workbench/): Story can only be called via Workbench and cannot run autonomously (i.e. scheduled actions will not run and the webhook will not accept events from outside of Workbench). Story will not be counted towards license count.

## Sending to a sub story

To send data to a sub-story, use a Send to Story action. Select the sub-story you would like to use and enter the required parameters.

![](https://www.datocms-assets.com/55802/1719338046-sts-action.png)

When this action runs, it will send the inputted values to the defined sub-story. The input action in the sub-story will then emit an event similar to the below:

```json
{
"webhook_action":{
  "#event_id":519633304,
  "#agent_id":398737,
  "body":{
    "ip_address": "8.8.8.8"
    }
  }
}
```

![](https://www.datocms-assets.com/55802/1719338599-webhook-event.png)

When this event reaches the defined output action, an event will be emitted at the calling Send to Story action that matches the Exit action's configuration.

In the example, the sub-story `Analyze IP Address` has the following data defined in its output action.

![](https://www.datocms-assets.com/55802/1719339458-output-event-2.png)

This same data is sent back to the Send to Story action that called the sub-story.

![](https://www.datocms-assets.com/55802/1719345397-sts-output.png)

## View all stories calling your sub-story

Within the overview pane of your sub-story, you can see all stories that you have access to that call your sub-story with a Send to Story action. In the example throughout this document, a story `Alert processing story` uses a send to story action to call the `Analyze IP address` sub-story. You can see this story listed within the sub-story as a story that calls it.

![](https://www.datocms-assets.com/55802/1719347048-story-call-2.png)

## Retrieving event data from Story runs using Send to Story

This feature is now documented under [Workflows as APIs](https://www.tines.com/docs/apis/).

### Disabling a Story

Disabling a story (or an individual action) instantly stops all of its events from propagating – even in the case of large [explodes](https://www.tines.com/docs/actions/types/event-transformation/explode/).



**

### Groups

## Overview

A **group** is a collection of actions in a story. Groups enable a better organization of complex stories by extracting well-defined branches of a story into a group.

## Creating a group

Create a group by selecting multiple actions and clicking "Group". You can also use the keyboard shortcut `Cmd/Ctrl + G`.

![](https://www.datocms-assets.com/55802/1715701578-screenshot-2024-05-14-at-16-41-54.png)

### Restrictions

1. Pages, Webhooks, and Receive Email actions cannot be grouped
2. Actions in a group cannot be scheduled

## Configuration Options.

- `payload`: Provide data that should be sent to the group. Include information from upstream events by specifying a wrapped JSONPath. 
- `loop`: (Optional) Specify the name of a field in an incoming event that contains a list.

## Inputs and Outputs

Each group will have both a group input and group output.

### Group Input

The group input defines custom fields that can be sent to the group, such as inputs. This allows you to easily pass data to your group from its parent story.

A group input can have more than one custom option, which must be one of the supported data types:

1. **Object**: Displays an object builder
2. **Plain Text**: Displays a plaintext input field
3. **Checkbox**: Renders the options as a single option checkbox
4. **Number**: Renders a number input field
5. **Formula**: Renders formula input field

![](https://www.datocms-assets.com/55802/1715701583-screenshot-2024-05-14-at-16-43-35.png)

### Group Output

The group output configures the data being returned from the group.

![](https://www.datocms-assets.com/55802/1715701588-screenshot-2024-05-14-at-16-45-05.png)

### Looping

You can also configure the action for array processing using the `LOOP` action option. Looping helps you to easily process an array and transform, filter or reduce incoming events.

By default, the loop will be processed **serially** in order to preserve the ordering of the array for event output. You can use the `Run loop in parallel` option in order to loop through the array elements in parallel, which offers better performance compared to serial looping, but doesn't guarantee that the order of the elements is maintained. 

- Specify the path to a field in an incoming Event that contains a list  and Tines will invoke the action for each element of the list.
- When specifying the output event payload, a `LOOP` object will be provided for each loop iteration. The `LOOP` object will contain:
  
  - `value` – The current value in the loop.
  - `index` – The current index in the loop.
- A single output event will  be emitted to the Group containing an aggregate of the loop results for the array.
- The payload of the output event will always be a list. It can potentially contain `NULL` elements.
- The loop option is only available on a top level Group. It cannot be configured for a nested Group.

##### Loop size limits

- A loop can only be ran on a list that contains fewer than `20,000` elements.
- If you wish to loop over a list that contains more than `20,000` elements, it is recommended that: 
  
  - The  [CHUNK_ARRAY](https://www.tines.com/docs/actions/formulas/functions/chunk-array) function is used to break the list into a list of smaller lists. 
  - An [Explode Mode](https://www.tines.com/docs/explode/) Event Transformation action is used to emit an event for each of the smaller lists.
  - The lists contained in each emitted event can be looped over without exceeding the loop size limit.

##### Loop error handling

- If one index in the array produces a failure, the loop processing is stopped and an error is logged / indicated to the user.
- Error path handling is required if it is necessary to proceed with loop processing so should be built into the story.

## Example Configuration Options

### Looping

Given the incoming Group below, process each element in the list and output an aggregated event result containing the payload message for each element in the list.

```json
{
  "numbers": [1, 2, 3]
}
```

```json
{
  "loop": "=numbers",
  "payload": "Message: This is index # <<LOOP.index>>, value=<<LOOP.value>>"
}
```

```json
[
    "message": "Message: This is index #0, value=1",
    "message": "Message: This is index #1, value=2",
    "message": "Message: This is index #2, value=3"
]
```

### Change Control

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

## Overview

By default, whenever you update a story in Tines, your changes immediately take effect. This works well for simpler use cases and especially for building out v1 of your workflow.

For up-and-running, mission-critical workflows, however, it's important to have greater oversight of changes. You need to be able to safely experiment with ideas for improvements and apply a complicated change-set in one fell swoop. That’s where Change Control comes in.

## Enabling change control

For any story, you can enable Change Control by using the **Switch** located at top right of the storyboard.

![](https://www.datocms-assets.com/55802/1737743302-cleanshot-2025-01-24-at-13-27-52.png)

Once enabled, your story is considered `LIVE` and read-only. No changes can be made to the story without first going through a review and approval process.

Tenant owners can specify that all stories created within a team or within a tenant have change control turned on by default. Only **Team admins** and users with the [custom permission](https://www.tines.com/docs/custom-roles/) `change control: manage` can toggle change control off on a story. See [**Change Control Settings**](#change-control-settings) for further details.



## Using change control

Once change control is enabled on a story, a user will follow the below steps to update that story.

### Create a draft

A draft is an editable copy of your live story. Users can freely make changes and test those changes within their draft without impacting the live, running story.

To create a draft, select the arrow next to `Select a draft` in the change control header and click `+` button in the drafts section. You can also view and select any existing drafts previously created by you or other users in your team. Drafts are collaborative, meaning multiple users can view and make changes in one draft. Additionally, users can create multiple drafts.

![](https://www.datocms-assets.com/55802/1752839718-screenshot-2025-07-18-at-12-54-55.png)

### Build and test your changes

You can build inside a draft as you normally would in Tines. 

#### Send external data to a specific draft

While building and testing your changes in Tines, you may want to send external data to your draft rather than to the live story. To do this, send your data to the unique URLs associated with your story's webhook, page, or receive email action.

- **Webhooks**: webhook URLs in drafts include a parameter `?draft=mydraftname`. Easily locate and copy this URL by locating the webhook action in your draft and copying the `Webhook URL` section on the right hand action configuration panel.
  
  - Tip: If you want to run the equivalent webhook action in all your draft stories, add `?all_drafts=true` to the live webhook URL as a parameter. This will make the webhook run for any draft story that can run [autonomously](#limitations).
    
    
    
    The response will include `draft_ids` containing the IDs of the drafts that we ran the webhook for. If we skipped any drafts that could not run autonomously we will also include `skipped_draft_ids` in the response. 
- **Pages**: page URLs in drafts include a parameter `?draft=mydraftname`. Easily locate and copy this URL by locating the page in your draft and selecting `Copy link` from the toolbar below the page.
- **Receive email**: receive email actions in `Mode:Email` will have a unique email address per draft. Easily locate and copy this URL by locating the receive email action in your draft and copying the `Email address` section on the right hand action configuration panel.
- **Send to story**: this action sends data to a sub-story in Tines. If you would like to send data to a specific draft of a sub-story, configure the optional parameter `Send to draft` specifying the draft ID. Easily identify the draft ID with the key `SEND_TO_STORY`.

#### Use Live events in a draft

While you can send external data to a specified draft (as noted in the above section), you may also want to test using data from your live story. By default, live events are included on the events list in drafts. You can enable / disable live events by using the toggle in the events panel. 

In addition to viewing live events in the action events list, you can also 

1. Use live events to test an action in a draft
2. Re-emit a live event into a draft
3. Use live events in auto-complete suggestion in the action builder

**

#### Use different resources and credentials in a draft

Depending on your workflow development style, you may want to target independent test versions of some or all external services.

You can achieve this by enabling and configuring test values and metadata for your credentials and resources. Tines will automatically use the test credential or resource value when running in a draft environment and seamlessly switch to the live credential or resource value when running in the live environment.

![](https://www.datocms-assets.com/55802/1776689295-screenshot-2026-04-20-at-13-48-04.png)

![](https://www.datocms-assets.com/55802/1776689932-screenshot-2026-04-20-at-13-58-39.png)

Alternatively, you can also configure a different credential or resource by using our `META.story.is_draft` variable, and an `IF` function inside your action configuration. For example, to target your development Jira instance in Test, and your production instance in Live, you could use an expression like:

```
IF(META.story.is_draft, CREDENTIAL.jira_test, CREDENTIAL.jira)
```

### Request a review

Once you have built and tested your changes, request a review of those changes before they update the live story. To do this, select `View changes` in the upper right of the storyboard. This will generate a modal visualizing your changes on a per-action basis. Within this modal, you can:

- Review your changes made
- Add a name for your change request
- Add further details in the description of your change request

Select the button `Request review` once your request is appropriately filled out. 

> **IMPORTANT:** Once a review is requested, the draft is locked from further changes. To make further changes, cancel the request or create a new draft.

### Review changes

Once a review is requested, a notification is sent within the Tines UI and to the change control webhook, if configured. By default changes requests can be approved by an **Editor**,** Team Admin**, or a user with the [custom permissions](https://www.tines.com/docs/custom-roles/) `change control: review` or `change control: manage`. Approving requests can also be restricted to story owners with the aforementioned permissions. To require story owner approvals, add a story owner for the story and turn on the "Change request approval" toggle. Once this setting is enabled, only a story owner can disable it.

![](https://www.datocms-assets.com/55802/1752502751-screenshot-2025-07-14-at-15-18-05.png)

To review the changes, navigate to the draft and select `View request`. This will generate a modal visualizing the requested changes on a per-action basis along with the context of the request name and description. 

Within this modal, you can toggle the following settings:

- Show storyboard: shows a visual of the action in the storyboard that has been changed (on by default)
- Show positional changes: shows any changes to action positions as these changes are purely visual and cannot break a story's functionality (off by default)
- Show only changes: shows only the action elements that changed (on by default)

Once you have reviewed the changes, **reject**, **approve**, or **cancel** the request. Similar to when the review was requested, a notification will be sent within the Tines UI and the change control webhook, if configured, once one of the review actions is selected.

### Push changes

If the change request is approved, the changes can be pushed to the live story. Navigate to the draft and select `View request`. Select `Push` to update the live story with your changes. After pushing, your changes will be applied together in a single update.

Any in-flight story runs will immediately switch over to the new logic, consistent with how edits to non change-controlled stories are applied. The draft will be deleted as the changes are now applied in the live story.

### View previous changes

Within the live story, you can see previous change requests, as well as their attached descriptions, via [story versions](https://www.tines.com/docs/story-versioning/). 



## Active change requests

To view all active change requests on a team select the left hand menu and navigate to `Active change requests`. 



![](https://www.datocms-assets.com/55802/1752841329-screenshot-2025-07-18-at-13-21-51.png)

## Change control notifications

Change control notifications are managed using webhooks at the team level. A webhook event will be generated if the story change request is `created`, `cancelled`, `approved`, `rejected` or `pushed`. 

You can configure a webhook notification endpoint by selecting a non-personal team in the top left navigation menu, manage team, notifications. Then input a webhook URL under the Change control heading.

At the tenant level, audit logs capture all change control related operations as well.

> **TIP:** It is highly recommended that you configure a change control webhook to have better insights into all change activity across your team.



![](https://www.datocms-assets.com/55802/1692793688-screenshot-2023-08-23-at-13-28-03.png)

## Change control settings

Tenant owners can configure the following settings related to change control. Navigate to this section by selecting the left hand menu, and clicking Settings. Within the settings center, select Change control under the Configurations sub-menu.

- **Enable change control for all teams**: change control is automatically turned on when a story is created throughout the tenant.
- **Enable change control for selected teams**: change control is automatically turned on when a story is created within the specified teams.
- **Require approval for all changes**: this requires the user approving the change to be different than the user who requested the change. Users with Team Admin permissions can bypass this requirement for emergency circumstances. If bypassed, a notification is sent to the change control webhook. NOTE: users with [custom permissions](https://www.tines.com/docs/custom-roles/) `change control: manage` or `change control: review` do not have the ability to bypass changes.
- **Require name and description for all change requests**: a change cannot be requested for review if the name or description field is blank.



## Limitations

Drafts are only active and capable of receiving information while being worked on by users within Tines. Drafts can't run autonomously. After a period of inactivity (30 minutes), any action in a draft will no longer process requests.

## API endpoints

Some API endpoints have parameters that are related to drafts.

If an endpoint has a `draft_id` parameter, passing a draft ID (which can be found after `/drafts/` in the story's URL) means that the request will be applied to that draft.

Some endpoints also have a `mode` or `story_mode` parameter. These exist for backwards compatibility. If `mode` or `story_mode` is `LIVE` the request will be applied to the live story. If `mode` or `story_mode` is `TEST` then the request will be applied to a draft called `test` if such a draft exists. If `draft_id` is present, `mode` or `story_mode` will be ignored.

### Testing

### Introduction

Testing allows users to ensure the core functionality of their story has not broken when making changes. 

[](https://tines-2.wistia.com/medias/c9f98ph6h1)

### Save story runs

Save a story run to easily find testing data when making future updates to your story. We recommend saving a story run per branch in your workflow to capture all relevant paths an event could travel. Test or re-run your saved run in your story at any time, regardless of your set event retention

> **NOTE:** Interested in saved story runs? [Reach out to the team](https://www.tines.com/contact-support) to discuss adding the feature to your Tines tenant.

#### Save a story run

To save a story run, go to `Test` tab in the right hand side panel of your desired webhook action or receive email action, and select `Record`. Once set, the next input received by those actions and subsequent story run will be recorded and saved. Saved story run inputs are not restricted by the event retention period and will be available until you delete the saved run. There is a limit of 20 saved story run inputs per action. 

> **INFO:** Runs that include an explode action cannot be recorded.

**

Once a run is saved, you can view, rename, or delete the story run. Navigate to your action's right hand side panel, go to `Test` tab, hover over your saved run, select `Edit saved story run` to open a popup from where you can rename or delete your saved run.

![](https://www.datocms-assets.com/55802/1759138529-screenshot-2025-09-29-at-10-35-13.png)

In order to see the full run, from the popup select `View run and manage mocks`.

**

#### Run a saved story run

When you re-run a saved story run, the input received by the first action in your initial story run will be re-sent to that first action again. To re-run a saved story run, navigate to your action's `Test` tab, hover over your saved run and select `Edit saved story run` to open a popup from where you can re-run that recorded run.

In a story with change control enabled, only saved live runs are available in live mode. In drafts, builders can access live story runs and create saved draft runs. However, saved draft runs cannot be pushed to live. They must be re-recorded in the live story.

**

#### Running a saved story run with mock payloads

When re-running a saved story run, you may not want or need all the actions in the story to run again. Mock payloads allow for a story to run normally but use the same output payload from the event in the saved story run in place of an actual action run's event.

**Marking a payload to be mocked**

Go to Test tab, hover over your saved story run, select `Edit saved story run`, then on the popup select to `View run and manage mocks`. A modal with all the actions and payloads will show.  That saved story run will  feature a switch with the label `Use as mock payload` for each action's payload in a story run. Turning on this switch will result in that particular payload being used as the event for that action when that saved story run is re-run. This will prevent actions such as HTTP Request Actions or Send Email Actions from interacting with the outside world but will allow the story to continue running.
All actions marked as mocked will be displayed in the story run's popup for visibility.

**

![](https://www.datocms-assets.com/55802/1759139614-screenshot-2025-09-29-at-10-52-10.png)

### Testing

To test story changes, navigate to your action's `Test` tab, select your recorded run and click `Run test`.** **This will re-run the story with saved inputs in a sandbox environment, request payloads are built but the requests are not sent if the action is mocked**.**

> **IMPORTANT:**
> To use the testing feature the saved run must include at least one action that makes a request.  
>   
> Saved runs that were recorded prior to November 17, 2025 are not compatible with this feature and will need to be re-recorded.

> **INFO:** Runs that include an explode action cannot be tested.

**

The test results will appear in the `Tests` section, here you will see badges providing a quick overview of differences between the saved and test run.

![](https://www.datocms-assets.com/55802/1763375365-screenshot-2025-11-17-at-10-29-17.png)

Clicking on the test run will bring you to the test results page. In this page, you can see detailed differences in the requests sent between the recorded run and the test run.

Only actions that send data outside of the story (such as an HTTP request, record creation action, or case creation action) are compared to ensure core functionality is not removed from a story. Triggers and event transform actions are not included because changes to those actions will not clearly show the impact of a story's functionality. 

**

If a test did not pass, it is possible to see at a glance what actions differ in the recorded run and test based on the icons seen in the action panel and a summary in the top left corner. Clicking into these rows allows you to see the exact differences, so you can investigate what has changed between the runs and verify if the changes are expected or the story needs to be edited further.

Some of the actions contain fields like timestamps, which could be an expected difference but still show as a mismatch.

There could also be a scenario when an action is excluded when you did not expect it to be, in this case ensure that your story does not contain any deduplication that would not allow you to repeat the run with the exact same information.

![](https://www.datocms-assets.com/55802/1763375965-screenshot-2025-11-17-at-10-38-32.png)

![](https://www.datocms-assets.com/55802/1763377116-screenshot-2025-11-17-at-10-58-21.png)

#### Auto-Ignored Fields in Test Runs

Some non-editable fields are expected to vary between Recorded and Test Runs. To help you focus on meaningful differences, these fields are automatically ignored during diff comparison.

**Example**: The `event_id` field in a Send to Story action generates a unique value on every run. Previously, this would appear as a mismatch during testing. Now, it's automatically filtered out.

##### Fields Automatically Ignored

**Record Action**

- In `record_writers`:
  
  - `record_field` where `record_type == "TIMESTAMP"`

**Case Action**

- In `metadata`:
  
  - `case_opened_datestamp`
  - `created_story_guid`

**Send to Story Action**

- Root level:
  
  - `#event_id`
  - `#action_id`
- In `meta`:
  
  - `event_id`
  - `action_id`
  - `draft_id`
  - `story_run_guid`

**

### Time saved

## Overview

You can record metrics for a story in the `Time saved` block of the Story panel. These metrics can then be viewed on the Reporting page in the Reporting overview chart.

## How to set up time saved

### 1) Publish story to a team 

Metrics are only recorded for stories which are **published** in teams. You can publish a story through the button at the top-right of the Story page. This is relevant for stories within draft teams.

**

### 2) Set time saved metrics

Select the story name from the time saved block. This will open a popover of metrics with modifiable units and values.

**

### 3) View data

And that's it! As your stories with time saved metrics run, the accumulated data will be displayed on the Reporting overview chart on the reporting page. As more metrics are added, more categories will be added to this graph.

**

### Workflows as APIs

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

Using APIs it is possible to make a HTTP request to initiate a story run and receive event data back from the story. This can be configured via API settings:

![](https://www.datocms-assets.com/55802/1684156508-cleanshot-2023-05-15-at-14-14-51-2x.png)

Retrieving data can be achieved by making a HTTP request to the webhook entry action.

The endpoint will return the event data emitted by the exit action. For example:

```json

HTTP/1.1 200 OK

{ 
    "message":"The random number is: 4"
}
```

In the case where there are multiple exit actions, the event data from first exit action to execute will be returned. 

### Response body, status, and headers

By default, the payload of the exit action is returned in the webhook HTTP response body. You can gain more fine-grained control over the response by adopting an exit action payload convention. 

If your exit action contains the `status` and `body` keys and the `status` field is a valid HTTP response code, the `status` field will be used to set the HTTP status line and the `body` field will be used as the HTTP response body.

Furthermore, you can include a `headers` object to include custom HTTP response headers. For example, the following exit action payload will generate the HTTP response below.

```json
{
  "status": "202",
  "headers": {
    "X-Tines-Story": "<<META.story.name>>",
    "X-Tines-Change-Control-Test": "<<META.story.is_test>>"
  },
  "body": {
    "random_number": "The generated random number is <<generate_random_number.number>>"
  }
}
```

```json

HTTP/1.1 202 Accepted
X-Tines-Story: My Story
X-Tines-Change-Control-Test: true

{ 
    "random_number":"The generated random number is 4"
}
```

### Content types

Responses can be returned in a variety of formats. To request a specific format you can use the HTTP `accept` header to specify the content type you want to receive. We currently support the following data types:

- `application/json` - the default format
- `text/csv` - you can receive your array data in CSV format
- `text/plain` - for data that doesn't need any fancy formatting
- File handling - if you return binary or Base64 encoded files. you can specify the format to support many, many more data types like `image/png`, `application/pdf` or `audio/aac`

We also support wildcards and will default to JSON. 

Below is an example showing the difference between JSON and CSV:

```json
HTTP/1.1 200 OK
Accept: application/json

[["1","2","3"],["4","5","6"]]
```

```json
HTTP/1.1 200 OK
Accept: text/csv

1,2,3
4,5,6
```

You can hard code a specific content type and content disposition headers in your response action. 

For example, the following exit action payload will generate a HTTP response below with the content type always set to `application/pdf` and a content disposition of `attachment; filename=report.pdf`, so the file downloads with the name `report.pdf`.

```json
{
  "status": "202",
  "headers": {
    "Content-Type": "application/pdf",
    "Content-Disposition": "attachment; filename=report.pdf"
  },
  "body": "<<http_request.body.base64encodedcontents>>"
}
```

### Metadata

Included in the response will be a set of HTTP headers containing metadata about the status of the request, the event, and the action producing the data:

- `X-Tines-Status`: The status of the request, values include:
  
  - `data_received` - the story executed and data is included in the response
  - `ok` - the story is executing but did not return data in time
  - `error` - there was an error processing the request
- `X-Tines-Event-ID`: The ID of the event containing the data that was returned
- `X-Tines-Exit-Action-ID`: The ID of the exit action that produced the event
- `X-Tines-Limit-Reached`: If the request reached a limit, the limit that was reached will be included here
- `X-Tines-Response-Location`: a URL where the response will be available in JSON format if and when they become available.

### Limits

An Exit Action must generate an event within 30 seconds of the request being made in order to return event data in the response. Otherwise, a `HTTP/1.1 504 Gateway Timeout` response will be returned.

```json

HTTP/1.1 504 Gateway Timeout

{ 
    "limit_reached": "time",
    "response_url": "https://tenant.tines.com/webhook/path/secret/id"
}
```

The story run will continue after this timeout response is returned. If/when an Exit Action produces an event for this story run, the `response_url` returned in body of the timeout response can be visited to access the event data. The response URL can also be found in the `X-Tines-Response-Location` header.

There are also limits on the number of these requests that can be run simultaneously. In the cases where these limits are exceeded, a response of `HTTP/1.1 201 Created` will be returned, rather than a `200` status code, and the story will continue to run. 

More information is included in the `X-Tines-Status` and `X-Tines-Limit-Reached` HTTP headers.

### Notes

## Introduction

Tines Notes provide a way to document and share information about a story and specific actions taken within it by providing a space to add text and images. Notes are seen by all team members when viewing the storyboard. Common use cases for Notes include documenting intricate parts of a story, explaining unclear API requests, listing out required parameters for Send to Story actions, and associating workflows with external documentation for audit purposes.

![](https://www.datocms-assets.com/55802/1656002221-annotations_using_annotation-d4bb9a4f752b0b86cebd82a5734816fe.gif)

## Creating a note

Notes can be added to a story by clicking the "Add Notes" option on the left side of the storyboard and dragging a new Note into the story. Notes can also be added by selecting the storyboard and using the keyboard shortcut `n`.

Notes support formatting using the Markdown markup language. We support the [CommonMark](https://commonmark.org/help/) specification. Notes have a limit of `8000` characters.

![](https://www.datocms-assets.com/55802/1776423412-screenshot-2026-04-17-at-11-56-07.png)

### Sections

## Introduction

A **section **visually associates a set of actions. Sections improve understanding and clarity by visually organising actions, making complex workflows easier to follow and interpret.

![](https://www.datocms-assets.com/55802/1771504280-screenshot-2026-02-19-at-12-30-51.png)

## Creating a section

Sections can be added to a story by navigating through the "Tools" menu on the left side of the storyboard and dragging a new Section into the story. You can also use the keyboard shortcut `S`.

To resize a section, drag on the edges of the section to your desired height and width. You can also select the section color via the color picker in the menu bar.

**

### Story copilot

## Introduction

Story copilot is an AI-chat interface for the storyboard. It helps you understand, maintain, manage, and build stories using natural language.

**

## Use story copilot

To use Story copilot, [AI features](https://www.tines.com/docs/admin/ai/) must be enabled for your tenant. Open a story and click the `Story copilot` button in the bottom right corner of the screen. In the top menu bar of Story copilot, you can see previous conversation history, start a new conversation, or minimize the window.

Story copilot has two modes that can be used and changed mid-conversation:

- **Ask**: in ask mode, no changes can be made to the story. Use ask mode to understand a story or brainstorm future changes.
- **Build**: in build mode, changes can be made to the story. Changes can be reviewed and accepted or undone in the chat. When a change is undone, the story is reverted to the version saved prior to those changes.

## Track usage

Token usage per conversation can be viewed via the `i` icon within the message box. Further token and credit data is aggregated on a team level in the AI usage section of the reporting page. In audit logs, individual user actions with Story copilot are recorded.

### Roles and permissions

Story copilot inherits the permissions of the user prompting it. For example, a user who can only view stories will not be able to edit or make changes to a story through Story copilot.

### Models

Story copilot will use the ["Smart Model"](https://www.tines.com/docs/admin/ai/#multiple-ai-provider-support) configured for your tenant. For best results we recommend using the Tines default smart model. If you're using another AI provider, we recommend using one of the latest models from OpenAI or Anthropic.

### File attachments

Story copilot allows users to attach the following file types:

- Images: `PNG`, `JPEG`, `GIF`, `WEBP`
- Documents: `PDF`, `CSV`, `MD`, `TXT` , `DOC`, `DOCX`, `XLS`, `XLSX`, `YAML`

Attachment limits:

- Users can attach up to three files per message.
- Each attachment may be up to 4.5MB in size.

If you're using a provider other than the default Tines provider, supported file types may vary.

## Actions

## Introduction

Actions are building blocks of automation in Tines. Actions can be configured to perform a variety of tasks, for example: receiving alerts from 3rd-party systems, sending/receiving emails, and interacting with APIs. They receive and emit events, and can run on a schedule or upon receiving an event.

## Action Types

There are eight types of actions. Each action type is designed to perform a specific task. Each action type can receive and emit events to other action types. The action types and a brief description of their function is shown below. For a detailed description of each action, see their dedicated article.

| Action Type | Function |
| --- | --- |
| [Webhook Action](/docs/actions/types/webhook/) | The Webhook action will emit events it receives through webhooks (HTTP callbacks). |
| [HTTP Request Action](/docs/actions/types/http-request/) | The HTTP Request action sends HTTP requests using a variety of methods to a specified URL. |
| [Receive Email Action](/docs/actions/types/imap/) | The Receive Email action, formally known as the IMAP action, emits events when it detects new emails on an IMAP server or when emails are sent to a uniquely generated email address. |
| [Send Email Action](/docs/actions/types/email/) | The Send Email action sends emails to recipients specified in the action options. |
| [AI Agent Action](https://www.tines.com/docs/actions/types/ai-action) | The AI Agent action invokes a secure and private large language model (LLM) on demand. |
| [Event Transformation Action](/docs/actions/types/event-transformation/) | The Event Transformation action has several modes that modify the contents of received events. |
| [Condition Action](/docs/actions/types/condition/) | The Condition action compares the contents of a field from an incoming event with predefined rules. When the rules match, an event is emitted. Further configuration can emit an event when the rules do not match. |
| [Send to Story Action](/docs/actions/types/send-to-story/) | The Send to Story action sends events to another Tines story (the sub-story). After the sub-story has completed its actions, the Send to Story action will emit an event. |

### Configuration

Actions are configured from the properties panel on the right of the storyboard. This appears when you select an individual action. All actions share common configuration options, as well as options specific to the action.

All actions have three tabs: build, status, and logs. Webhook actions have an additional summary tab.

## Common Config

For all actions, you can:

- Configure the name, description, and options unique to the action type from the build tab.
- View the action status, set up action monitoring to notify you of events or errors, and [set up time saved per action](https://www.tines.com/docs/reporting/) from the status tab.
- View action logs to find out what happened from the log tab.

Depending on the action type, all options may not be available in the common config.

| Action type | Can be scheduled | Can receive events | Can emit events |
| --- | --- | --- | --- |
| [Send Email Action](/docs/actions/types/email/) | Yes | Yes | Yes |
| [Event Transformation Action](/docs/actions/types/event-transformation/) | Yes | Yes | Yes |
| [HTTP Request Action](/docs/actions/types/http-request/) | Yes | Yes | Yes |
| [Receive Email Action](/docs/actions/types/imap/) | Yes | No | Yes |
| [Condition Action](/docs/actions/types/condition/) | No | Yes | Yes |
| [Webhook Action](/docs/actions/types/webhook/) | No | No | Yes |
| [Send to Story Action](/docs/actions/types/send-to-story/) | Yes | Yes | Yes |
| **[AI Action](https://www.tines.com/docs/actions/types/ai-action/)** | Yes | Yes | Yes |

## Source and Receiver Actions

When an action runs, either on a schedule or when an event is received from a source action, it will generate a new event and emit it to receiving actions.

As shown below, by dragging a string between actions, we define where events should be emitted. 

**Failure path for actions**

A failure path can be defined for actions. This will cause any action run which contains an error log to emit its event down the failure path. To opt into the behavior, use the action context menu and select "set failure path", and then link to another action to receive failures.

**

## Adding options

All of the basic options required for an action to function will be shown when you first drag an action onto the storyboard. However, most actions have additional optional options which allow you to achieve more with the action. You can access these options by clicking on the add option button at the bottom of the properties panel.

![](https://www.datocms-assets.com/55802/1655988134-quickstart_creating_an_action_additional_options-7b104f7c1d13ca9881c90dffd1762ce4.png)

### Common Options

Every action has the following options, as well as others more specific to that action type:

| Option | Description |
| --- | --- |
| 
Customize output

 | 

This option allows you to modify an action's output via the `OUTPUT` key. This is useful for verbose actions like HTTP Request Actions, which produce a lot of metadata that you might not need, bloating events and making them harder to parse. With Customize output you can filter out any unnecessary information and just surface the values you need.

**N.B.** The `OUTPUT` key is a special case just for the **Customize output** option (and the below **log error if** option). As a result, in previews it will always be `null`. You will need to run the action to see the actual output. You cannot reference the contents of the **Customize output** object via the `OUTPUT` key, as `OUTPUT` only refers to the original output of the action, before **Customize output** is applied.

**N.B.** When a Webhook action is invoked as the entry point of a Send to Story (whether from a Send to Story action, an AI Agent tool, or Workbench) the customize output option is not applied. In this context, the incoming payload represents the story's input parameters, not an HTTP response, so the transformation is skipped to preserve the integrity of the data passed between stories.



 |
| Emit failure event | This option provides a guarantee that an event will be emitted – even if a fatal error takes place, such as a formula expression which crashes. In those cases, an event with `{"failed": true}` will be emitted, allowing you to intercept and recover as required. |
| Local values | 

This object is generated once per run and it can be referenced elsewhere in the action via the `LOCAL` key. It functions like a set of variables or cached values for the action, and it can be used to make your action options more readable or to save time by calculating a value only once and reusing it in multiple places.

You can reference `LOCAL` from within the **Local values** object but it will only be able to refer to keys that were defined earlier, otherwise they will resolve to `null`.



 |
| Log error if | Similar to the previous option customize output, this option allows you to log an error based on the action’s output – accessible with the `OUTPUT` key in a formulas expression. It is similar to the [HTTP request action’s log error on status option](https://www.tines.com/docs/actions/types/http-request/#:~:text=Defaults%20to%2025.-,log_error_on_status,-%3A%C2%A0%5B0%2C%20400), but more flexible and available to all actions. |

> **NOTE:** Pages are a special case. While they support **Local values** and **Customize output**, they do not have an **Options** user interface. If you want to use these options in a page, you need to open the **Editor** view and add them to the JSON directly via the "local" and "customize\_output" keys.

### Action Metadata

In addition to the common options, many actions also include the **Include action metadata** option. When enabled, this adds a `meta` attribute to emitted events, containing metadata about the action run. This metadata is only added if the event is an object.

The following actions support metadata: [HTTP Request](https://www.tines.com/docs/http-request/), [Send Email](https://www.tines.com/docs/email/), [Condition](https://www.tines.com/docs/condition/) and [Event Transformation](https://www.tines.com/docs/event-transformation/) when used in [Automatic](https://www.tines.com/docs/automatic/), [Deduplicate](https://www.tines.com/docs/deduplicate/), [Explode](https://www.tines.com/docs/explode/), [Extract](https://www.tines.com/docs/extract/), or [Message only](https://www.tines.com/docs/message-only/) modes.

All actions that support metadata include the following attributes, though some actions may provide additional metadata as well.

| Attribute | Description |
| --- | --- |
| duration | 
The total time, in seconds, that the action took to complete. It measures the time from when the action started running to when an event was created, including formula evaluation, network requests, and any other internal processing.

 |
| pending\_duration | 

The total time, in seconds, that an action was pending. It measures the time from when the action became eligible to run until processing began.

 |
| response\_time (HTTP Request action only) | 

The total time, in seconds, from when a request is sent to when a response is received. Useful for identifying slow requests or monitoring the performance of external APIs.

 |

![](https://www.datocms-assets.com/55802/1755883502-screenshot-2025-08-22-at-1-22-49-pm.png)

> **NOTE:** If the event already includes a `meta` attribute, any available metadata will be merged into the existing `meta` (when possible) without overwriting existing values.

#### Config Events and Values

## Working with Events and Values

Every Tines event is a JSON object. The data within events can be accessed in action configurations using JSONPaths.

```json
{
  "customerName":"John Doe",
  "address":
  {
    "streetAddress":
    {
      "number":123,
      "street":"Sample Street"
    },
    "city":"Example Town"
  }
  "orders":
  [
    {
      "orderId":23284,
      "itemName":"Widget",
      "itemPrice":33.99
    },
    {
      "orderId":63122,
      "itemName":"Gadget",
      "itemPrice":22.50
    },
    {
      "orderId":77284,
      "itemName":"Sprocket",
      "itemPrice":12.00
    }
  ]
}
```

To insert information from an incoming event into an action's options block, first insert a value pill:

![](https://www.datocms-assets.com/55802/1655988183-actions_configuration_add_pill-76c13542664c33a233beaf3a5737186b.gif)

This will display a popup where you can add the value you want to be inserted into this location

![](https://www.datocms-assets.com/55802/1655988199-actions_configuration_pill_editor-628da78409cd5638ef86b9ad4547516d.png)

Within this popup typing the following will insert the value of the customerName key from the above, sample Tines event

`customerName`

```
John Doe
```

To access information in a nested JSON key, use the following syntax:

`key_name.subkey_name`

The following will insert the value of the city key from the above, sample Tines event.

`address.city`

```
Example Town
```

### Accessing Data in Nested Keys

You can query further levels of subkeys using the following syntax.

`address.streetAddress.street`

```
Sample Street
```

### Accessing Data in Arrays

Arrays are queried using an array index expression inside square brackets ([]). For example, the following wrapped JSONPath can be used to access the first element of the "orders" array:

`orders[0]`

```json
{
  "orderId":23284,
  "itemName":"Widget",
  "itemPrice":33.99
}
```

The following wrapped JSONPath will return the `itemPrice` from the 2nd element of the orders array.

`orders[1].itemPrice`

```json
{
  "orderId":63122,
  "itemName":"Gadget",
  "itemPrice":22.50
}
```

> **IMPORTANT:** It is often necessary to process every element of an array individually, this can be achieved using an [Event Transformation Action](/docs/actions/types/event-transformation) in 'explode' mode.

### Types

#### Webhook

The Webhook Action will emit Events it receives through Webhooks (HTTP callbacks).

Webhooks are a common way for web applications to notify users of important occurrences. Use the Webhook action to receive alerts from SIEM; get notified when a developer performs a 'git push'; when a user updates a Jira ticket; or when a user posts in Slack.

To create a new webhook address, simply drag on a Webhook Action to create a URL which can receive HTTP data.

**

## 
Features

- Each Webhook Action has a unique URL.
- Specify a path and a secret that must be included in the Webhook in order for an Event to be emitted.
- Accept Webhooks using a variety of HTTP methods, e.g.: POST and GET.
- Webhook parameters will be used to generate and emit a new Event.
- Specify a custom response message, response code and response headers when Event is successfully emitted.
- Include incoming headers from HTTP requests.

## Configuration options

- `path` - A path for the webhook URL, in plain text.
- `secret` - (Optional) A token that the host will provide for authentication.
- `access_control` (Optional) Whether the webhook should be public, available only with the secret, OAuth, or using Tines API Keys (see Authentication section below).
- `verbs` - (Optional) Comma-separated list of HTTP verbs your action should accept.
- `response` - (Optional) The response message to the request. Defaults to 'Ok'.
- `response_code` - (Optional) The HTTP response code to the request. Defaults to `201`.
- `response_headers` - (Optional) An object with any custom response headers. (example: `{"Access-Control-Allow-Origin": "*"}`)
- `include_headers` - True by default, include headers from the request in a `headers` key while the body of the request is nested under a `body` key.
- `match_rules` - Specify the rules to match against body or headers in the request.
- `must_match` - Specify the number of rules that must match.
- `capture_raw_body` - Capture and emit the raw body of the incoming request.
- `rate_limit` - Rate limit requests to the webhook. See the Rate Limiting section below.

## Emitted events

The Webhook Action will convert the Webhook payload into a Tines Event.

Technical notes

- When an array, boolean, string, or anything besides an object is sent as the request payload and the content type is JSON, (e.g., from an HTTP Request Action with a JSON content type), the resulting event from the webhook is automatically wrapped in an object with a `_json` key. The `body` object will contain `{"_json": [...]}` instead of the array directly. To access the array, use `body._json`.
- When making requests with headers who's keys contain dashes `-` or capital letters in them to webhooks, the resulting events generated from the webhook will interpolate those dashes as underscores* *and downcase the letters in the keys only, values remain unchanged (e.g., an HTTP Request Action with a header key: `X-Example-Id` and value: `AB-C` will have an event emitted by the webhook with `"x_example_id": "AB-C"`.

## Authentication and authorization

There are a few methods for authenticating a webhook. The primary one is using webhook secrets: a random string assigned to each webhook.

Another option is to use OAuth, scoping access to either the Team or the Tenant. If you select OAuth, the user will be redirected to your Tines tenant to grant consent when adding the MCP server to their client.

Alternatively, you can set the Access Control for the webhook to be "Team" or Tenant", and use Tines API Keys passed in via the `Authorization` HTTP header.

### Secrets in URL

By default, webhook requests are authenticated by the Webhook Action `secret`. This is passed to the Webhook Action via as part of the webhook URL: `https://tenant.tines.com/webhook/<path>/<secret>`.

Secrets are not part of the path for webhook actions with an [API path prefix](https://tines.com/docs/actions/types/webhook/#api-path-prefix). You can just remove the secret field from the action if you don't want a secret to be required for this endpoint. To provide a secret in the URL for those stories, you can use a query param: `https://tenant.tines.com/api/public/<api_name>?secret=<secret>`

### Access control

There are four access control options

- Public: Anyone with the path (no secret or authentication is enforced)
- Secret: Anyone with the secret (default method)
- Team: Only team members (regardless of role)
- Tenant: Members of this Tines tenant (regardless of role)

When configuring Team or Tenant access control, the only way to authenticate is to provide a valid Tines API Key with each request. When an API Key is provided, the email of the API Key's owner will be attached to the `email` key in the `headers` of the emitted event.

> **IMPORTANT:** If you created a Tines API key using a connect flow and want to use it for webhooks, you need to add an extra allowed URL for `<your-tenant>/webhook/*` on the credential restrictions.

### Authorization HTTP header

If you wish to use the HTTP `Authorization` header you can pass the Webhook Action `secret`  or API Key (depending on the access control opion) as the credential using a `Basic` or `Bearer` auth scheme:

```bash
curl -H "Authorization: Bearer <secret>" https://tenant.tines.com/webhook/<path>
```

### Signatures

An alternative to sending the `secret` with every request is to sign your webhook request with the `secret`. The process to signing each request is as follows:

- Generate a timestamp, milliseconds or seconds from epoch works great.
- Concatenate the timestamp into a String of the format `<timestamp>.<webhook URL>.<request body>` where:
  
  - `timestamp` is the timestamp in numeric format. For example, `1686567186`
  - Followed by the `.` character
  - `webhook URL` is the full URL of the webhook, including query parameters. For example, `https://tenant.tines.com/webhook/<path>`
  - Followed by the `.` character
  - `request body` is the raw body of a HTTP POST request. This can be left blank for HTTP GET requests
- Compute an HMAC of the concatenated String with the SHA256 hash function. Use the Webhook Action `secret` as the key.
  
  - For example, you could use the `HMAC_SHA256()` [Tines function](https://www.tines.com/docs/formulas/functions/hmac-sha256/) or the Open SSL example below.
- Add the timestamp and the result to the `X-Tines-Signature` HTTP header in the format: `ts=<timestamp>;sig1=<hmac>`.



Here are some examples for HTTP GET and POST requests:

**GET**

```bash
TS=$(date +%s)

URL="https://tenant.tines.com/webhook/<path>?foo=bar"

SIG=$(echo -n "$TS.$URL." | openssl dgst -sha256 -hmac <secret>)

curl $URL -H "X-Tines-Signature: ts=$TS;sig1=$SIG"
```

**POST**

```bash
TS=$(date +%s)

URL="https://tenant.tines.com/webhook/<path>"

BODY="{'foo': 'bar'}"

SIG=$(echo -n "$TS.$URL.$BODY" | openssl dgst -sha256 -hmac <secret>)

curl -X POST $URL -H "X-Tines-Signature: ts=$TS;sig1=$SIG" -d $BODY
```

## Example configuration options

Receive GET and POST requests when the correct `path` and `secret` is supplied

![](https://www.datocms-assets.com/55802/1715094369-screenshot-2024-05-07-at-16-05-20.png)

```json
{
  "path": "eb7f40dbc217bfd8c4a5a843611a3b2d",
  "secret": "c9e648cdf08204c3aa9784fe2efa2e61",
  "verbs": "get,post"
}
```

Receive POST requests and respond with a custom `response`, `response_code `and `response_headers`. Response values and headers can be added by selecting them from the options dropdown.

![](https://www.datocms-assets.com/55802/1715094523-screenshot-2024-05-07-at-16-07-19.png)

![](https://www.datocms-assets.com/55802/1715094607-screenshot-2024-05-07-at-16-10-00.png)

```json
{
  "path": "eb7f40dbc217bfd8c4a5a843611a3b2d",
  "secret": "c9e648cdf08204c3aa9784fe2efa2e61",
  "verbs": "get,post",
  "response": "Thank you!",
  "response_code": 200,
  "response_headers": {
    "X-Tines-Response": "Event emitted"
  }
}
```

Respond with data contained in a resource. Allowed content types are `text/plain`, `text/xml`, and `application/json`.

```json
{
  "path": "eb7f40dbc217bfd8c4a5a843611a3b2d",
  "secret": "c9e648cdf08204c3aa9784fe2efa2e61",
  "verbs": "get,post",
  "response_headers": {
    "content-type": "application/json"
  },
  "response": "<<RESOURCE.ip_list>>"
}
```

Respond to webhook verification challenges using data received by the webhook in the request headers or body. In the example below, the `headers` key refers to the headers from the incoming request. Similarly, `body` can be used to access the body of the incoming request.

![](https://www.datocms-assets.com/55802/1715094686-screenshot-2024-05-07-at-16-11-13.png)

```json
{
  "path": "eb7f40dbc217bfd8c4a5a843611a3b2d",
  "secret": "c9e648cdf08204c3aa9784fe2efa2e61",
  "verbs": "get,post",
  "response": {
    "verification": "<<headers.x_okta_verification_challenge>>"
  }
}
```

The feature described above is useful when the incoming request contains information that it expects to receive in the response. For example, [some Slack API endpoints](https://api.slack.com/events/url_verification) include a `challenge` field. Unless the response to that request includes the same `challenge` field, slack will not trust the webhook. You can fulfil this requirement by accessing the challenge value through the `body` field.

```json
{
  "path": "eb7f40dbc217bfd8c4a5a843611a3b2d",
  "secret": "c9e648cdf08204c3aa9784fe2efa2e61",
  "verbs": "get,post",
  "response": {
    "challenge": "<<body.challenge>>"
  }
}
```

Redirect requests to another address while still recording the data payload.

```json
{
  "path": "eb7f40dbc217bfd8c4a5a843611a3b2d",
  "secret": "c9e648cdf08204c3aa9784fe2efa2e61",
  "verbs": "get,post",
  "response": "https://www.google.com",
  "response_code": "302"
}
```

## Response-enabled webhooks

In stories with the** **`Enable webhook API responses`** **[option](https://www.tines.com/docs/stories/apis/) enabled, the response from the webhook will be event emitted from the first `Exit action` reached in the story, as long as that action is executed within 30 seconds.

No more than 100 webhook-enabled concurrent responses can be processed simultaneously (1000 in dedicated tenants). When this limit is exceeded, the webhook action will fall back to responding immediately with the `response` it has configured.

### API path prefix

Response-enabled webhooks also have the option to provide an `API path prefix`. This special option allows the response-enabled webhook to be called via an API-style path: `https://tenant.tines.com/api/public/<api-path-prefix>`. You can include subpaths, and they will be included in webhook action's event data at `body.api_path`.

## Custom domain for webhooks

> **TIP:** To enable custom domains on your webhooks (and pages), [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

With a custom domain enabled, all webhooks (including webhooks in existing stories built prior to the configuration of a custom domain) will be switched to the custom domain for their webhooks. However, the webhooks will still be accessible via their original URL. Please refer to [Custom domain for pages](https://www.tines.com/docs/custom-domain-for-pages/) for information about how custom domains affect pages.

## Webhook rules

Webhook Rules allow you to specify criteria that must be met in order for an event to be emitted. The Tines Webhook Action can parse the request body and/or headers to meet these criteria. An event is created only when a rule is matched, and the matching rule is included in the `meta` object of the event. If no rule is matched, a `No Rules Matched` response is returned to the client.

In the following example, we are checking if an HTTP header named `X_HTTP_EXAMPLE` is equal to the value `foo` or if the JSON request body has a nested object where the key `foo` equals the value `bar` using dot notation.

```json
{
  "path": "4d4217f7e94467ee3dec675189566f9a",
  "secret": "a409cd0d15a079ee69873aaa5b2e3d04",
  "verbs": "get,post",
  "must_match": 1,
  "rules": [
    {
      "type": "field==value",
      "value": "foo",
      "path": "headers.X_HTTP_EXAMPLE"
    },
    {
      "type": "field==value",
      "value": "bar",
      "path": "body.object_example.foo"
    }
  ]
}
```

*Demo*

## Rate Limiting

Requests to your webhook action can be rate limited using any attribute from the HTTP headers and/or body. This is a great way to ensure that your webhook is not overwhelmed by requests. The `rate_limit` action option takes three parameters:

• `key`: The value to track. This can be from headers, body, or any formula.

• `count`: The maximum number of requests allowed for the value of `key`, for the time window specified in `period`.

• `period`: The time window in seconds during which it will check the count for the evaluated key.

The rate limit is checked for every request, and if the count is exceeded the request is rejected with a default `429` status code.

#### Receive Email 

The Receive Email Action, formerly known as the IMAP Action, emits Events when it detects new emails on an IMAP server or when emails are sent to a uniquely generated email address.

Use the Receive Email Action to automate actions based on received emails, for example: analyze potentially malicious emails for threats; auto-respond to customers based on email subject or body; or alert teammates in Slack when particular emails are received. To trigger the action, send an email to the email address listed on the action.

![](https://www.datocms-assets.com/55802/1677018907-cleanshot-2023-02-21-at-16-33-23.png)

## Features

The Receive Email action has two modes of operations:

- [Email mode](https://www.tines.com/docs/email/) - the action emits Events when it detects emails sent to a uniquely generated email address (default).
- [IMAP mode](https://www.tines.com/docs/imap/) - the action emits Events when it detects new emails on an IMAP server.

##### Email mode

The Receive Email Action in Email mode emits Events when its uniquely generated email address receives an email.

Emails received at this generated email address are securely stored in an AWS S3 bucket per region, with strict tenant isolation enforced at the application level. Each tenant’s emails are only accessible to that tenant and are never visible to any other tenant. All emails are automatically deleted after **7 days.**

The S3 buckets are not publicly accessible, and access controls are in place to ensure that only the Tines application can read or delete emails on behalf of the correct tenant.

## Features

When this mode is enabled:

- Receive emails at a uniquely generated email address.
- Emails received will be parsed and emitted as a JSON event.

This mode is unavailable for self-hosted deployments at this time.

> **NOTE:** We filter out emails with duplicate message IDs so they will not trigger a new story run.

## Configuration Options

- `mode`: A string denoting the Receive Email Action's mode of operation, in this case: `email`.
- `include_rfc822`: Optionally include the raw message, in [RFC 822 format](https://datatracker.ietf.org/doc/html/rfc822), in emitted events (under the `rfc822` key).

## Emitted Events

Events emitted by the Receive Email Action in Email mode look similar to the below:

```json
{
  "message_id": "1688375064.8603887.1514928714437@example.com",
  "subject": "Example email subject",
  "from": "bob@example.com",
  "to": ["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@your-tenant.tines.email"],
  "cc": ["carol@example.com"],
  "date": "2022-07-30T10:10:00+00:00",
  "mime_type": "text/plain",
  "body": "This is the body of the email.",
  "has_attachment": true,
  "attachments": [
    {
      "filename": "hello.txt",
      "guid": "dee73fe0-044f-4e2d-873e-e6850debc03a",
      "md5": "aba2d86ed17f587eb6d57e6c75f64f05",
      "sha256": "807126cbae47c03c99590d081b82d5761e0b9c57a92736fc8516cf41bc564a7d",
      "sizeinbytes": 1578,
      "base64encodedcontents": "ug4AtAnNIbgBTM0hVGhpc=="
    }
  ],
  "headers": {
    "Return-Path": "<0102018249942bec-8efe779d-3d23-4b5a-80bf-6de509bd1d36-000000@ses.tines.io>",
    "Received": "from a4-7.smtp-out.eu-west-1.amazonses.com (a4-7.smtp-out.eu-west-1.amazonses.com [54.240.4.7]) by inbound-smtp.eu-west-1.amazonaws.com with SMTP id g4o04bs0cv1b2sfrj2ksjgo7aep0pikllbom2no1 for 25b051dc47529b8da96a8b97d4ce811c@crimson-dream-3714.tines.email; Fri, 29 Jul 2022 10:50:24 +0000 (UTC)",
    "Date": "Sat, 30 Jul 2022 10:00:00 +0000",
    "From": "Bob <bob@example.com>",
    "Reply-To": "bob@example.com",
    "To": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@your-tenant.tines.email",
    "Message-ID": "<1688375064.8603887.1514928714437@example.com>",
    "Subject": "Example email subject",
    "Mime-Version": "1.0",
    "Content-Type": "multipart/alternative; boundary=\"--==_mimepart_62e3bb6f70048_17911c249ef\"; charset=UTF-8",
    "Content-Transfer-Encoding": "7bit",
    "Received-SPF": "pass (spfCheck: domain of ses.tines.io designates 54.240.4.7 as permitted sender) client-ip=54.240.4.7; envelope-from=0102018249942bec-8efe779d-3d23-4b5a-80bf-6de509bd1d36-000000@ses.tines.io; helo=a4-7.smtp-out.eu-west-1.amazonses.com;",
    "Authentication-Results": "amazonses.com; spf=pass (spfCheck: domain of ses.tines.io designates 54.240.4.7 as permitted sender) client-ip=54.240.4.7; envelope-from=0102018249942bec-8efe779d-3d23-4b5a-80bf-6de509bd1d36-000000@ses.tines.io; helo=a4-7.smtp-out.eu-west-1.amazonses.com; dkim=pass header.i=@amazonses.com; dkim=pass header.i=@tines.io; dmarc=pass header.from=tines.io;",
    "X-SES-RECEIPT": "AEFBQUFBQUFBQUFIM1d5R25FQWEwZVl3cldyM0N5QVNtcXQwakZ0OElBWVZZcCs4QnhLOTFUYSt3dnl5cmpxd0pnazczRVhPdmJieEFJZCtHR2FjdmorSmN3dXh3cEpaTnVEZDZrQTducC9nTVBwOFF0ZjByVW9zL1hScFlDcjFKSHRzcFoxUkVYdnFpck13Z3RTRVNzWGRFYWNCRmhiQkVXZDB5NmhrbXNBTTZORU9KS1o4eXNYUVRTTCtZVytXSmhwSWdiRDJiWDkydXFac01IRWgxbFd6Y1A2NmtpN3NycW4wUURFNEt3TkpzUWtmQytJZDFhZ3dOTXAweCs1ODNGcFh0NXJQOGEwdHJLaGdpVE5wcGtXaTVZZlo3UXlzNXIrQk81UDJBVHpYcS9oM1piWVlIZXlOUkNTOW41WDFFMVB4cklGVXFIam9tZ3VOWm1VTmlVbS9QNUFCVC9YN2NwUU5VME1WUGQ4MUIvMkZKclJTR0UxbGpPQXB0eGpiUk1kR0IwVFVyV1NBPQ==",
    "X-SES-DKIM-SIGNATURE": "a=rsa-sha256; q=dns/txt; b=k2FDIklOe39G+MKIQxJ4/dejuB7EMvUKWeGcRfMWznqTnuXJ9M8cDGnFHkwIC29whWFdmKBUTAVcILNB6q8v8DhsNjDNhY/UcLG2005xKAfQFrvABHedpEz6gFPbWNys5tA+meXL0rqM72m2rOhgjfK63qYPcZyMGer5z/01DXI=; c=relaxed/simple; s=ihchhvubuqgjsxyuhssfvqohv7z3u4hn; d=amazonses.com; t=1659091824; v=1; bh=bGgV2fB3HAxOUg9dChQkEWlnZvs2p0GZ7jZMoRqpfGA=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;",
    "DKIM-Signature": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;\ts=nmks7xlzimxketb3gcructunub7exko2; d=tines.io; t=1659091824;\th=Date:From:Reply-To:To:Message-ID:Subject:Mime-Version:Content-Type:Content-Transfer-Encoding;\tbh=bGgV2fB3HAxOUg9dChQkEWlnZvs2p0GZ7jZMoRqpfGA=;\tb=fDcV9Xbh3+Sv7A3v1wdoE9Dh8m6VOgXEQXMuoU5azfS8KsBlEYyOmlVlKUaHBs2/\tJFzRq3tNf7akOJfZtG54d1W4aizvm46OfAlCOMud0KRRc6lqucK3eP946/Uyb4QJS72\thHnuGnl2Y8acvguucVYc4CJQ3DGLnEEUHdTfsNY8=\nv=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple;\ts=ihchhvubuqgjsxyuhssfvqohv7z3u4hn; d=amazonses.com; t=1659091824;\th=Date:From:Reply-To:To:Message-ID:Subject:Mime-Version:Content-Type:Content-Transfer-Encoding:Feedback-ID;\tbh=bGgV2fB3HAxOUg9dChQkEWlnZvs2p0GZ7jZMoRqpfGA=;\tb=0VNvTHFmFjB5D/nvfj7BnRUfHHTxEsvlxXzZd5L27VdxWhTAKomnbMtBvjupnpyL\tVi9Evat//z2AOVEFb8IvVXOJW/RAa57HU6U77H58fyumroqnYQnTFH2lFG4+7MOvasc\tTE8Zrv4GOwSMbG6tG/9GYMwFtRT1pitszRkBLtJA=",
    "X-Tines-Agent-ID": "684413",
    "X-Tines-Domain": "your-tenant.tines.com",
    "Feedback-ID": "1.eu-west-1.sWFTxVcM3fWwhwNysR+tYHH7k79VuWibpja2yta+Ak4=:AmazonSES",
    "X-SES-Outgoing": "2022.07.29-54.240.4.7"
  }
}
```

## Example Configuration Options

```json
{
  "mode": "email"
}
```

##### IMAP mode

The Receive Email Action in IMAP mode emits Events when it detects new emails on an IMAP server.

Use the Receive Email Action to automate actions based on received emails, for example: analyze potentially malicious emails for threats; auto-respond to customers based on email subject or body; or alert teammates in Slack when particular emails are received.

## Features

- Check for new emails manually or run on a schedule.
- In the first visit to a folder, only check for the initial status and do not emit Events.
- Only emit Events when new emails meet certain conditions.
- Optionally mark emails as "read" after checking IMAP server.
- Specify the folder to check for new emails.
- Keep a list of Message-Id's for 100 most recent emails, so duplicate emails do not emit multiple events.

> **NOTE:** Marking a message as unread in your mailbox will not trigger a new email story run since we filter out emails that have already triggered a story run. We also filter out emails with duplicate message IDs.

## Configuration Options

- `host`: Enter the host of the IMAP server.
- `username`: Enter the username used to authenticate to the IMAP server.
- `password`: Enter the password used to authenticate to the IMAP server. Remember, never include sensitive details such as passwords directly in Action configurations. Instead, use the [`CREDENTIAL` formulas key](https://www.tines.com/docs/formulas/referencing-data#from-credentials) (see example configuration options below).
- `port`: (Optional) Enter the port used to connect to the IMAP server.
- `ssl`: (Optional) When this key is set to "true", the Action will connect to the IMAP server using SSL.
- `folders`: Define an array of folder names to monitor for new emails.
- `mark_as_read`: (Optional) When this key is set to "true" it will mark detected emails mails as read on the IMAP server. Set as "false" by default.
- `disable_ssl_verification`: (Optional) Set to 'true' to disable ssl verification. Set as "false" by default.
- `use_tunnel`: (Optional) If you have one or more Tines Tunnels configured, you can choose to send IMAP traffic from this Action through a Tunnel.
- `mode`: (Optional) A string denoting the Receive Email Action's mode of operation, in this case: `imap`. A Receive Email Action defaults to [Email mode](https://www.tines.com/docs/email/) when this field is not provided.
- `authentication_type`: (Optional) Selects the authentication type used for the IMAP connection. The default `LOGIN` type can be changes to `XOAUTH2` in order to use OAuth2 credentials - when using this type you can set an OAuth2 credential as the password to connect successfully.
- `mime_types`: Allows you to set the sort order of the parts, overriding the default sort order. Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content type coming after.
- `include_rfc822`: Optionally include the raw message, in [RFC 822 format](https://datatracker.ietf.org/doc/html/rfc822), in emitted events (under the `rfc822` key).

## Emitted Events

Events emitted by the Receive Email Action in IMAP mode look similar to the below:

```json
{
  "message_id": "1688375064.8603887.1514928714437@example.com",
  "folder": "INBOX",
  "subject": "This is the subject of the email",
  "from": "bob@example.com",
  "to": ["alice@example.com"],
  "cc": ["carol@example.com"],
  "date": "2018-01-01T10:10:00+00:00",
  "mime_type": "text/plain",
  "body": "This is the body of the email.",
  "has_attachment": true,
  "attachments": [
    {
      "filename": "hello.txt",
      "guid": "dee73fe0-044f-4e2d-873e-e6850debc03a",
      "md5": "aba2d86ed17f587eb6d57e6c75f64f05",
      "sha256": "807126cbae47c03c99590d081b82d5761e0b9c57a92736fc8516cf41bc564a7d",
      "sizeinbytes": 1578,
      "base64encodedcontents": "ug4AtAnNIbgBTM0hVGhpc=="
    }
  ]
}
```

## Example Configuration Options

Create Events for all emails from the Inbox of a GMail/GSuite account:

```json
{
  "host": "imap.gmail.com",
  "username": "alice@gmail.com",
  "password": "<<CREDENTIAL.gmail>>",
  "ssl": true,
  "folders": ["INBOX"],
  "conditions": {}
}
```

Create Events for emails sent to an Office365 account where the subject contains "Urgent" (case-insensitive):

```json
{
  "host": "outlook.office365.com",
  "username": "alice@outlook.com",
  "password": "<<CREDENTIAL.outlook>>",
  "ssl": true,
  "disable_ssl_verification": true,
  "mark_as_read": true,
  "folders": ["INBOX"],
  "conditions": {
    "subject": "(?i)urgent"
  }
}
```

Create Events for emails from any @example.com address which also include an attachment:

```json
{
  "host": "outlook.office365.com",
  "username": "alice@outlook.com",
  "password": "<<CREDENTIAL.outlook>>",
  "ssl": true,
  "folders": ["INBOX"],
  "conditions": {
    "from": "*@example.com",
    "has_attachment": true
  }
}
```

Create Events for all emails from the Inbox of a GMail/GSuite account, connecting through OAuth2:

```json

{
  "host": "imap.gmail.com",
  "username": "alice@gmail.com",
  "password": "<<CREDENTIAL.gmail_oauth2>>",
  "ssl": true,
  "folders": ["INBOX"],
  "conditions": {},
  "authentication_type": "XOAUTH2"
}
```

#### Send Email

The Send Email Action, formerly Email Action, sends emails to recipients specified in the Action options.

Use the Send Email Action to notify individuals of important occurrences using data from upstream Events. For example alert team members of malicious behavior detected in firewall logs; notify engineers of the results of a vulnerability scan; notify employees of suspicious activity related to their accounts. 

## Features

- Send emails on receipt of events.
- Send emails in text or HTML format.
- Customize `body` and `subject` of email using the Action options.
- Emails will be sent from [mail@tines.io.](mail@tines.io)
- Use [pages](https://www.tines.com/docs/pages/) to automate recipient responses.
- As well as sending the email to recipients, an Event containing send status will be emitted by the action.

## Limits

Community Edition tenants are limited to sending 200 emails per day. Reach out to the Tines team if your would like to request lifting this limit.

## Configuration Options

- `recipients`: The email address (or an array of email addresses) to which the email should be sent.
- `reply_to`: (Optional) An email address to which any replies to the email should be sent.
- `cc`: (Optional) An email address (or an array of email addresses) that should be CC'ed on the email.
- `bcc`: (Optional) An email address (or an array of email addresses) that should be BCC'ed on the email.
- `subject`: A subject for the email. Include information from upstream events by inserting value pills.
- `body`: (Optional) Customize the body of the email. Include information from upstream events by inserting value pills. The body can contain simple HTML and will be sanitized. When using `body`, it will be wrapped with `<html>` and `<body>` tags, so these do not need to be added.
- `content_type`: (Optional) Provide a content type for the email by specifying `text/plain` or `text/html`. If you do not specify `content_type`, then the recipient email server will determine the correct rendering.
- `attachments`: (Optional) Send one or more attachments as part of the email. For each file, specify `filename`, its contents encoded in base64 `base64encodedcontents` and `content_type`, in an object. The `content_type` is "auto" by default and automatically infers the content-type of the file.
- `sender_name`: (Optional) Display name of the email's sender, e.g. `Alice Munro <mail@tines.io>` instead of `Tines <mail@tines.io>`.
- `capture_rfc822`: (Optional) Include the delivered message, in [RFC 822 format](https://datatracker.ietf.org/doc/html/rfc822), in emitted events (under the `rfc822` key).

## Emitted Events

Events emitted by the Email action look similar to the below:

```json
{
  "status": "Success"
  "mail": {
    "to": "bob@example.com",
    "reply_to": "alice@example.com",
    "sender_address": null,
    "sender_name": "Alice Bob",
    "cc": [],
    "bcc": [],
    "subject": "Example email from Tines",
    "body": "A real email body could go here",
    "headers": null,
    "content_type": null,
    "advanced_html": true,
    "attachments": []
  }
}
```

## Example Configuration Options

Send a simple email to one recipient:

```json
{
  "recipients": "alice@example.com",
  "subject": "Example email from Tines",
  "body": "A real email body could go here"
}
```

Send a simple email to one recipient and specify that replies should be sent to a specific address:

```json
{
  "recipients": "alice@example.com",
  "reply_to": "bob@example.com",
  "subject": "Example email from Tines",
  "body": "A real email body could go here"
}
```

Send an HTML email to multiple recipients including Event data from an upstream Action:

```json
{
  "recipients": ["alice@example.com", "bob@example.com"],
  "subject": "Vulnerability scan completed",
  "body": "The scan has completed. <br /><br /><b>Hosts scanned:</b> <<host_count>><br /><b>Vulnerabilities detected: </b> <<vulnerability_count>>",
  "content_type": "text/html"
}
```

Send an attachment as part of an email:

```json
{
  "recipients": "thomas@tines.xyz",
  "subject": "See Attached",
  "body": "See attached the file I mentioned",
  "attachments": [
    {
      "filename": "<<download_file.body.filename>>",
      "base64encodedcontents": "<<download_file.body.base64encodedcontents>>"
    }
  ]
}
```

#### HTTP Request

HTTP requests can be sent to a specific URL using various HTTP methods.

Use a HTTP Request Action to interact with REST APIs and web applications. For example: find all tweets mentioning specific keywords; update a JIRA ticket based on incoming Events; create a Pager Duty incident; check VirusTotal for an MD5 hash.

## Features

- Send requests on receipt of incoming Events, or run on a schedule.
- Send requests using 'GET', 'POST', 'PUT', 'PATCH' and 'DELETE' methods.
- Enable/disable SSL verification. TLS 1.2 and 1.3 are supported.
- Optionally specify HTTP headers, including user agent. 
- Include information from incoming Events in HTTP requests
- Request response will be emitted as a new Event

## Configuration Options

By default, all HTTP Request actions include [action metadata](https://www.tines.com/docs/actions/configuration/#action-metadata) under the key, `meta`.

- `url`: Specify where the request should be sent. Include the URI scheme ('http' or 'https').
- `basic_auth`: (Optional) Specify HTTP basic auth parameters: "username:password", or ["username", "password"].
- `content_type`: (Optional) Specify the content type to use with the request. When set, Tines will perform smart validation of HTTP request payload to fit the content type. If this is not desired behavior, set a content type header using the `headers` option instead. Shorthands are provided for the following, common content types:
  
  - 'application/json; charset=utf-8': `json`
  - 'text/xml; charset=utf-8': `xml`
  - 'application/x-www-form-urlencoded': `form`
  - 'multipart/form-data': `data`
- `custom_certificate_authority_name: (Optional) Specify the name of the custom certificate authority to use for this request. Defaults to 'default', if present`
- `customize_output: (Optional) Control the event data this action produces. Note that event and LOCAL data are not available here.`
- `disable_redirect_follow`: (Optional) Disables automatic following of redirects during HTTP requests. When false, only the results of the last request, as a result of the last redirect are shown. False by default.
- `disable_ssl_verification`: (Optional) Set to 'true' to disable ssl verification.
- `disable_url_encoding`: (Optional) Disable automatic URL encoding of non-URL-safe characters in the URL and query parameters. False by default.
- `emit_failure_event`: (Optional) Control when events are emitted for error conditions without setting up failure paths. 
  
  - Always: Emit events for any logged error. If `retry_on_status` is configured, a single failure event will be emitted after all retries have been exhausted.
  - Never: Never emit events for logged errors (story run will stop). 
  - Error response only (HTTP Request actions only): Emit events for HTTP status code errors (404, 500, etc.) but not transient connection failures (timeouts, DNS errors). This is the default behavior for HTTP Request actions. See also `retry_on_status` and `log_error_on_status`.
- `headers`: (Optional) Specify a hash of headers to send with the request.
- `https_only`: (Optional) Require that all requests use the HTTPS protocol, including redirects.
- `local_values`: (Optional) A place for performing upfront calculations, which can then be re-used from the LOCAL object.
- `log_error_if`: Log an error if the given formula is true, based on the output event data. Note: this option is evaluated separately from `log_error_on_status` (they are not mutually exclusive). 
- `log_error_on_status`: `[0, 400-499, 500-599]` by default. Specify the array of status codes that should cause an error to be logged. If the HTTP response received by the action has one of these codes, then an error will be logged.  Note: if the action is configured to both `log_error_on_status` and `retry_on_status` for a status code, it will first retry and log that it failed but is retrying, but no error will be logged until all retries are exhausted. 
  
  - Each array element can be either a single status code (e.g. 400), or a range of status codes (e.g. 400-499). Ranges are inclusive of starting and ending values.
- `method`: (Optional) Specify the HTTP method to use, i.e.: `get`, `post`, `put`, `patch`, or `delete`. Defaults to `post`.
- `mutual_tls`: (Optional) Credentials to use mutual TLS for the request. Must be an object with the following keys:
  
  - `root_certificate`: The root certificate for the certificate authority (CA) responsible for signatures.
  - `client_certificate`: The certificate issued by the CA for this client.
  - `client_private_key`: The private key for the client certificate. For convenience, this can also be an interpolated [Mutual TLS credential](https://hub.tines.com/docs/credentials/mtls) containing the required information.
- `use_ntlm_authentication`: (Optional) For connecting to older Windows-based systems. When enabled, set the username and password using basic auth.
- `pagination`: (Optional) Automatically paginate through the results of the request.
  
  - `has_more`: Boolean condition to indicate if there are more results to fetch. The first request will be made regardless of the condition value. Subsequent requests will only be made while this condition is `true` AND the request count is below the limit of 100.
    
    - An example of a `has_more` condition might be a formula that checks if the `current_page` is less than `total_pages` in the response.
    - [PAGINATE variables](https://tines.com/docs/formulas/referencing-data/#paginate), which hold data about the pagination run, might be helpful to use in the formula.
  - `output_as_single_event`: If output as single event is true, outputs all page responses as a single event once the last request has completed.
    
    - The maximum size limit for each page is 10 MB.
    - The combined event is still subject to the 100 MB size limit.
    - When an error occurs, such as an HTTP error status code or a connection error, the pagination run will stop short and emit an event with responses up to and including the error response.
- `params_encoder`: (Optional) When set to 'flat' allows duplicate query parameters and when 'nested' duplicate params are not permitted. When 'nested', query params included in the payload are added to the query using array syntax eg. `/example?key[]=value_1&key[]=value_2`. Defaults to 'nested'.
- `parse_response_as`: (Optional) Choose how responses are converted into event data, the default is to infer this from the response's Content-type header. Available options are:
  
  - `text`: Treat the response as plain text
  - `json`: Attempt to parse the response as JSON
  - `xml`: Attempt to parse the response as XML
  - `file`: Store the response as a file object with base64 encoded contents
- `payload`: (Optional) Specify key-value parameters to include in the body of the request. Use wrapped JSONPaths to include data from incoming Events.
- `http_proxy_url`: (Optional) Direct your requests through a specified proxy URL.
- `query_params`: (Optional) Specify key-value query parameters to be appended to the URL of the request. Query params can be used with both flat and nested query param formats, and with or without url encoding. Use wrapped JSONPaths to include data from incoming Events.
- `redirect_authorization`: (Optional) Retain the authorization header when following a redirect to a different hostname.
- `retries`: (Optional) Specify the maximum number of retries. This does not affect the interval of retries, only the number that are performed. Defaults to 25. Enabling `retries` on the action will not trigger retries to run, it only sets the number of retries to be made. **Note: **`retry_on_status` *must* be enabled.
- `retry_on_status`: (Optional) Specify the array of status codes that should cause a retry. If the HTTP response received by the action has one of these codes, then it will be retried. 
  
  - Each array element can be either a single status code (e.g. 400), or a range of status codes (e.g. 400-499). Ranges are inclusive of starting and ending values.
  - The retry schedule consists of 25 retries with exponential back-off plus random "jitter", starting at 5 seconds after the initial failure and gradually increasing to 10 minutes after the most recent failure, ie. `[5, 10, 20, 40, 80, 160, 320, 600, 600, ...].` The jitter added is of random duration and up to `(10 * (retry_count + 1))` seconds. Total back-off time over 25 retries is approx. 3h 20mins, `[5 * (2**retry_count), 10 * 60].min + (rand(10) * (retry_count + 1))`. If `emit_failure_event` is configured, a failure event will not be emitted for each unsuccessful retry. A single failure event will be emitted after all retries have been exhausted.
- `send_get_payload_as_body`: (Optional) For HTTP GET requests, rather than sending the payload as URL params (e.g. `https://example.com/search?parameter=value`), send as the request body.
- `timeout`: (Optional) Specify the timeout of the HTTP request in seconds. Defaults to `30` seconds.
- `use_tunnel`: (Optional) If you have one or more Tines Tunnels configured, you can choose to send requests from this Action through a Tunnel.
- `rules`: (Optional) Specify the rules to determine if this action executes.
- `must_match`: (Optional) Specify the number of rules that must match for the action to execute.

## Emitted Events

Events emitted by the HTTP Request Action will include the 'body', 'headers' and response 'code' from the returned response. For example:

```json
{
  "body": "ok",
  "headers": {
    "Date": "Mon, 1 Jan 2018 10:10:00 UTC",
    "Content-Type": "text/html; charset=utf-8",
    "Transfer-Encoding": "chunked",
    "Connection": "keep-alive",
    "Set-Cookie": "__cfduid=df0297dac2e4057e71e36fb67009723e91519037460; expires=Tue, 01-Jan-19 10:10:00 UTC; path=/; domain=.example.com; HttpOnly",
    "Via": "1.1 vegur",
    "Strict-Transport-Security": "max-age=15552000",
    "X-Content-Type-Options": "nosniff"
  },
  "status": 200,
  "meta": {
    "response_time": 0.08232
  }
}
```

## Example Configuration Options

*The below samples use the postman-echo.com utility.*

Send a simple GET request:

```json
{
  "url": "https://postman-echo.com/get?foo1=bar1&foo2=bar2'",
  "method": "get"
}
```

Send a POST request with data from an incoming Event:

```json
{
  "url": "https://postman-echo.com/post",
  "content_type": "json",
  "method": "post",
  "payload": {
    "user": "alice",
    "title": "<<person.title>>",
    "age": "85"
  },
  "headers": {}
}
```

Retry a request on 429 & 5xx errors, and log errors for other 4xx errors:

```json
{
  "url": "https://postman-echo.com/post",
  "content_type": "json",
  "method": "post",
  "payload": {
    "user": "alice"
  },
  "retry_on_status": ["429", "500-599"],
  "log_error_on_status": ["400-428", "430-499"]
}
```

Send a request to a service that requires Basic authentication (password is accessed using the [`CREDENTIAL` formulas key](https://www.tines.com/docs/formulas/referencing-data#from-credentials)), include a custom header:

```json
{
  "url": "https://postman-echo.com/basic-auth",
  "method": "get",
  "headers": {
    "X-Tines-Request": "123456"
  },
  "basic_auth": "postman:<<CREDENTIAL.postman>>"
}
```

Submit a file emitted as an attachment from an IMAP action to Virustotal using the `data` method:

```json
{
  "url": "https://www.virustotal.com/vtapi/v2/file/scan",
  "content_type": "data",
  "method": "post",
  "payload": {
    "file": {
      "contents": "<<BASE64_DECODE(get_email_with_attachment.attachments[0].base64encodedcontents)>>",
      "filename": "<<get_email_with_attachment.attachments[0].filename>>"
    },
    "apikey": "<<CREDENTIAL.virustotal>>"
  },
  "headers": {}
}
```

Note, in the above example, the `file` key underneath payload can be called anything and is used as the form name argument in the body of the request e.g. `image` or `document`. The action will find any such object with the child keys `contents` and `filename` set.

Additional parameters that do not need to be included in the HTTP boundary framing should be included as query parameters in the URL instead of the payload of the request e.g. `https://slack.com/api/files.upload?channels=<<channel_id>>&initial_comment=<<comment>>&filetype=<<filetype>>`.

### Egress IP addresses

The egress IP is the IP address that external servers or services see as the source of an HTTP Request action's request. To see the egress IP addresses for your tenant, visit `https://<tenant-domain>/info`.

### Scheduling

A HTTP Request can be scheduled to run at a specific time, interval & timezone. This can be configured using the `Scheduling` menu as demonstrated below:

![](https://www.datocms-assets.com/55802/1715077334-screenshot-2024-05-07-at-11-22-05.png)

This can also be injected via the JSON payload for the Action using the `schedule` object. See example:

```json
{
  "schedule":[{"cron":"0 8 * * *","timezone":"Europe/Dublin"}]
}
```

### Rules

Rules allow you to specify criteria that must be met in order for an event to be emitted. The Tines HTTP Request action can parse the event data from upstream action to meet these criteria. An event is created only when a rule is matched, and the matching rule is included in the `meta` object of the event. If no rule is matched, a log line is emitted.

In the following example, we are checking if a message from an upstream event transaction action matches a string.

```json
{
  "path": "4d4217f7e94467ee3dec675189566f9a",
  "secret": "a409cd0d15a079ee69873aaa5b2e3d04",
  "verbs": "get,post",
  "must_match": 1,
  "rules": [
    {
      "type": "field==value",
      "value": "Error detected",
      "path": "<<event_transformation_action.message>>"
    },
  ]
}
```

*Demo*

### Pagination

An HTTP Request action with pagination enabled will make multiple requests in a loop until the `has_more` formula option evaluates to `false`. By default, `has_more` is set to the formula `PAGINATE.index < 10` , which means exactly 10 requests will be made because `PAGINATE.index` starts at 0 and will increment by 1 after each request. However, you will likely want to set this formula to a condition based on the response contents, something like "current page < total number of pages". To achieve this, the `PAGINATE.previous_response` variable can be used to access data from the last page response of the current pagination run. For example, if the API response looks like this:

```json
{
  "body": {
    "agents": [
      ...
    ],
    "meta": {
      "current_page": "https://test.tines.com/api/v1/actions?per_page=20&page=1",
      "previous_page": null,
      "next_page": "http://test.tines.com/api/v1/actions?per_page=20&page=2",
      "next_page_number": 2,
      "per_page": 20,
      "pages": 5,
      "count": 91
    }
  },
  "status": 200
}
```

We can set the `has_more` condition to the following formula

```
IS_PRESENT(PAGINATE.previous_response.body.meta.next_page_number) 
```

In this API, `next_page_number` will be null when there are no pages left, but this will of course vary depending on which API is being used.

> **NOTE:** `PAGINATE.previous_response` will be `null` on the first request, but since the `has_more` condition is ignored on the first request, we don't need to worry about it evaluating to `false` .

Next, we will need to set the request parameters so that the pagination advances to a new page on each iteration. You must make sure the parameters change on each request, otherwise you will end up requesting the same page over and over until the max page limit is hit. Using this example, we could set the `page` query parameter to

```
PAGINATE.previous_response.body.meta.next_page_number 
```

This formula will evaluate to `null` on the first iteration, which will get us the first page. On subsequent iterations, it will evaluate to increasing page numbers until it reaches `null` for the last page and pagination will stop. Here are the final action options:

```json
{
  "url": "https://<<INPUT.tenant_domain>>/api/v1/actions",
  "method": "get",
  "headers": {
    "Authorization": "Bearer <<INPUT.tines_credential>>"
  },
  "content_type": "application_json",
  "payload": {
    "page": "<<PAGINATE.previous_response.body.meta.next_page_number>>"
  },
  "pagination_options": {
    "enabled": true,
    "has_more": "<<IS_PRESENT(PAGINATE.previous_response.body.meta.next_page_number)>>",
    "output_as_single_event": false
  }
}
```

By default, a pagination-enabled HTTP Request action will emit an event for each page, but you may optionally choose to output all pages as a single event by setting `output_as_single_event` to `true`.

#### Condition

The Condition Action tests an incoming Event against a set of predefined rules. These rules determine the flow of the story by comparing an incoming value against a test value.

Some common use-cases for the Condition Action include:

- Ignoring Events that don't require processing
- Send Events down different paths for additional, custom analysis

> **INFO:** This action was formerly known as Trigger. To ensure correct flow of workflows, it will continue to be referenced as Agents::TriggerAgent in the API.

## Rule types

- `matches regex`: The incoming value matches a defined regular expression.
- `does not match regex`: The incoming value doesn't match a defined regular expression.
- `is equal to`: The incoming value is equal to the test value.
- `is null`: The incoming value is null or empty.
- `is not null`: The incoming value is not null or empty.
- `is not equal to`: The incoming value is not equal to the test value.
- `is less than`: The incoming (numeric) value is less than the test value.
- `is less than or equal to`: The incoming (numeric) value is less than or equal to the test value.
- `is greater than`: The incoming (numeric) value is greater than the test value.
- `is greater than or equal to`: The incoming (numeric) value is greater than or equal to the test value.
- `contains`: The incoming value contains the test value. If the incoming value is an array, the test value must exactly match one of the items in that array — partial matches are not supported. Matching works for text, numbers, booleans, and null values, but not for objects. If the incoming value is text, the test value must be a text substring found within it.
- `does not contain`: The incoming value does not contain the test value. If the incoming value is an array, the test value must not match any of the items in that array. If the incoming value is text, the test value must not be found within the incoming text.
- `formula is true`: The result of the formula expression is true.
- `formula is false`: The result of the formula expression is false.

## Configuration Options

- `customize_output`: (Optional) Control the event data this action produces. Note that event and LOCAL data are not available here.
- `emit_failure_event`: (Optional) Control when events are emitted for error conditions without setting up failure paths. 
  
  - Always: Emit events for any logged error. 
  - Never: Never emit events for logged errors (story run will stop). 
  - Error response only (HTTP Request actions only): Emit events for HTTP status code errors (404, 500, etc.) but not transient connection failures (timeouts, DNS errors). This is the default behavior for HTTP Request actions.
- `emit_no_match`: (Optional) By setting `emit_no_match` to `true` an event will also be emitted if the rules do not match.
- `local_values`: (Optional) A place for performing upfront calculations, which can then be re-used from the LOCAL object.
- `log_error_if`: (Optional) Log an error if the given formula is true, based on the output event data.
- `must_match`: (Optional) By default, all rules must match for the Action to emit an Event. You can switch this so that only one rule must match by setting `must_match` to '1'.
- `rules`: The rules array contains sets of `type`, `path` and `value` fields.
  
  - `type`: `regex`, `!regex`, `field==value`, `field!=value`, `field<value`, `field<=value`, `field>value`, `field>=value`, `in`, `not in`, `formula` or `not formula`
  - `path`: Specify the incoming value, used as the left side of the comparison. Can be a path to a value or a more complex formula.
  - `value`: Specify the test value, used as the right side of the comparison.

## No match branch

If instead of halting the story run you'd like to redirect it depending on a rule, you can use the **no match branch** to send the story in a different direction depending on the result. This is a useful pattern, equivalent to an "if/else" flow in a software language.

## Emitted Events

When a rule is matched, Events emitted by the Condition Action will contain a field named `rule_matched` set to "true".

```json
{
  "rule_matched": true
}
```

When a rule is not matched and `emit_no_match` is configured, Events will be emitted by the Condition Action and will contain a field named `rule_matched` set to "false". 

If `emit_no_match` is not configured and a rule is not matched, these Events will still be emitted, but they will be labeled `No match`.

```json
{
  "rule_matched": false
}
```

## Example Configuration Options

Emit an Event when the contents of the 'size' field in an incoming Events are greater than 5.

```json
{
  "rules": [
    {
      "type": "field>value",
      "path": "<<size>>",
      "value": "5"
    }
  ]
}
```

![](https://www.datocms-assets.com/55802/1758038381-screenshot-2025-09-16-at-16-59-22.png)

Emit an Event when the content of the 'body' field contain an email address.

```json
{
  "rules": [
    {
      "type": "regex",
      "path": "<<body>>",
      "value": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b"
    }
  ]
}
```

![](https://www.datocms-assets.com/55802/1716390632-screenshot-2024-05-22-at-16-10-05.png)

Emit an event when 'dog' is found in the array of animal types listed in `path`.

```json
{
  "rules": [
    {
      "type": "in",
      "path": ["dog", "bird", "turtle"],
      "value": "dog"
    }
  ]
}
```

![]()

Value can also be an array. In this case the action will emit an event when either 'dog' or 'cat' is found in the array of animal types listed in `path`.

```json
{
  "rules": [
    {
      "type": "in",
      "path": ["dog", "bird", "turtle"],
      "value": ["dog", "cat"]
    }
  ]
}
```

![](https://www.datocms-assets.com/55802/1716390810-screenshot-2024-05-22-at-16-13-11.png)

Emit an Event when 'dog' is not contained in the array of animal types listed in `path`.

```json
{
  "rules": [
    {
      "type": "not in",
      "path": ["bird", "turtle"],
      "value": "dog"
    }
  ]
}
```

![](https://www.datocms-assets.com/55802/1716390851-screenshot-2024-05-22-at-16-13-59.png)

Value can also be an array. In this case it means that neither 'dog' nor 'cat' are contained in the array of animal types listed in `path`.

```json
{
  "rules": [
    {
      "type": "not in",
      "path": ["bird", "turtle"],
      "value": ["dog", "cat"]
    }
  ]
}
```

![](https://www.datocms-assets.com/55802/1716390896-screenshot-2024-05-22-at-16-14-42.png)

Emit an Event when the contents of the 'size' field of an incoming Event is greater than 0 **AND** the contents of the 'username' field is "alice".

```json
{
  "rules": [
    {
      "type": "field>value",
      "path": "<<size>>",
      "value": 0
    },
    {
      "type": "field==value",
      "path": "<<username>>",
      "value": "alice"
    }
  ]
}
```

![](https://www.datocms-assets.com/55802/1716391064-screenshot-2024-05-22-at-16-17-27.png)

Emit an Event when the contents of the 'size' field of an incoming Event is greater than 0 **OR** the contents of the 'username' field is "alice".

```json
{
  "rules": [
    {
      "type": "field>value",
      "path": "<<size>>",
      "value": 0
    },
    {
      "type": "field==value",
      "path": "<<username>>",
      "value": "alice"
    }
  ],
  "must_match": 1
}
```

![](https://www.datocms-assets.com/55802/1716391110-screenshot-2024-05-22-at-16-18-21.png)

When receiving the following event:

```json
{
  "student": {
    "name": "Alice",
    "age": "20"
  }
}
```

Emit an event if the student is over 25 years of age:

```json
{
  "rules": [
    {
      "type": "field>value",
      "value": "25",
      "path": "<<student.age>>"
    }
  ]
}
```

![](https://www.datocms-assets.com/55802/1722525420-screenshot-2024-08-01-at-16-16-51.png)

#### Event Transform

The Event Transform action has several modes of operation that either modify the contents of incoming events, or affect behavior of the workflow.

For example, you could:

- Extract all URLs from an email body (with **message only mode** and [`REGEX_EXTRACT`](https://www.tines.com/docs/formulas/functions/regex-extract)).
- Emit individual events for all IP addresses from a SIEM alert (with **explode mode**).
- Ignore processing information already seen recently (with **deduplicate mode**).

For tenants created after Jan 8th 2025 there is a limit of 10 minutes for Event Transform actions.

## Features

The Event Transform Action has several modes of operation:

- [Message only mode](https://hub.tines.com/docs/actions/types/event-transformation/message-only)
- [Explode mode](https://hub.tines.com/docs/actions/types/event-transformation/explode)
- [Extract mode](https://hub.tines.com/docs/actions/types/event-transformation/extract)
- [Deduplicate mode](https://hub.tines.com/docs/actions/types/event-transformation/deduplicate)
- [Delay mode](https://hub.tines.com/docs/actions/types/event-transformation/delay)
- [Implode mode](https://hub.tines.com/docs/actions/types/event-transformation/implode)
- [Throttle mode](https://hub.tines.com/docs/actions/types/event-transformation/throttle)
- [Automatic mode](https://www.tines.com/docs/automatic/)

## Common options

- `rules`: (Optional) Specify the rules to determine if this action executes.
- `must_match`: (Optional) Specify the number of rules that must match for the action to execute.
- `include_metadata`: (Optional) Adds a `meta` key to the event with details about the action run. This applies to automatic, deduplicate, explode, extract and message_only modes. Learn more about this option in Action Metadata [docs](https://tines.com/docs/actions/configuration/#action-metadata).

### Rules

Rules allow you to specify criteria that must be met in order for an event to be emitted. The Event Transform action can parse the event data from upstream action to meet these criteria. An event is created only when a rule is matched, and the matching rule is included in the `meta` object of the event if `include_metadata` is turned on.  If no rule is matched, a log line is emitted.

In the following example, we are checking if a status from an upstream HTTP Request action matches the value.

```json
{
  "path": "4d4217f7e94467ee3dec675189566f9a",
  "secret": "a409cd0d15a079ee69873aaa5b2e3d04",
  "verbs": "get,post",
  "must_match": 1,
  "rules": [
    {
      "type": "field==value",
      "value": "200",
      "path": "<<http_request_action.body>>"
    },
  ]
}
```

##### Deduplicate mode

Specify a field in an incoming events and only emit the event if that field contains a unique value.

## Features

- Define how many historical events from previous story runs should be used to determine if value in field is unique.
- Specify a period of time to check for duplicates.
- Specify the field in an incoming event to use as a measure of uniqueness.
- If an event is deemed unique, it will be emitted. If it is deemed a duplicate, it will be ignored and a corresponding log entry will be created.

## Configuration Options

- `mode`: 'deduplicate'
- `path`: a JSON wrapped path the value of which should be used to determine the uniqueness of the event.
- `lookback`: (Optional: must use either `lookback` or `period`) Number of past events (maximum of 1000) to examine for uniqueness.
- `period`: (Optional: must use either `lookback` or `period`) Number of seconds to use for deduplication. When an event is received, subsequent events will not be emitted until this period has elapsed.
- `emit_duplicates`: (Optional) Set to true if duplicate events should be emitted. `'unique_event': false` will be included in emitted event.
- `tolerance`: (Optional) The number of events with the same value to allow before deduplicating. Events of the same value will be treated as unique until the provided tolerance is reached.

## Emitted Events

```json
{
  "unique_event": true
}
```

## Example Configuration Options

Check the last 100 received events and only emit an event if the value in the `person.name` field is unique. To see this on your storyboard, copy the JSON below into an event transform action by selecting `View code` at the top right of the action and pasting the JSON in.

```json
{
  "mode": "deduplicate",
  "lookback": "100",
  "path": "<<person.name>>"
}
```

##### Delay mode

The Event Transformation Action delay mode pauses the story execution for specific number of seconds before emitting an event.

Delays are a good way of slowing down the execution of your story. Particularly useful when your story makes request to a rate-limited external service. Delaying retries to the external service could help prevent you from hitting their rate-limits.

## Features

- Specify a value in seconds to wait before emitting a received event.
- Delayed events are placed in a queue until they are emitted. Events will be dropped instead of being added to the queue if there are 20,000 or more events in the queue already. 

**Note:** The 20,000 delayed events queue is per action. A large queue for one action does not impact other actions.

## Configuration Options

- `mode`: 'delay'
- `seconds`: (Optional: must use either `seconds` or `until`) an integer greater than 0.
- `until`: (Optional: must use either `seconds` or `until`) a date/time in the future at which point the event will be emitted. The input can be specified using the following syntax: [https://github.com/mojombo/chronic?tab=readme-ov-file#examples](https://github.com/mojombo/chronic?tab=readme-ov-file#examples)

## Emitted Events

Events emitted by the Event Transformation Action in `delay` mode look similar to the below:

```json
{
  "seconds": 25
}
```

## Example Configuration Options

Wait 10 minutes before emitting an event.

```json
{
  "mode": "delay",
  "seconds": 600
}
```

Wait 7 days before emitting an event.

```json
{
  "mode": "delay",
  "until": "7 days from now"
}
```

## UI Configuration 

When an Event Transformation action is added its option defaults to `Seconds`. Alternatively `Until` can be configured as an option via the action options menu as shown below:

 

![](https://www.datocms-assets.com/55802/1725452393-screenshot-2024-09-04-at-13-19-14.png)

##### Explode mode

Specify a field in an incoming Event that contains an array and Tines will emit individual Events for each element of the array.

## Features

- Specify a field containing an array in an incoming Event.
- A new Event will be emitted for each element of the array.
- Specify the name of the key to hold the individual array elements in the emitted Event.
- The newly emitted Event will also contain an `index` key describing the exploded element's position in the original array.
- If the specified field is not a valid array, no Event will be generated.

## Configuration Options

- `mode`: 'explode'.
- `path`: When using 'explode' mode, define the wrapped JSON path for the field containing an array of elements to be emitted as individual events.
- `to`: Specify the name of the field to contain the array of matches.
- `limit`: (Optional) an integer representing the maximum number of events that can be emitted. Defaults to 500.

## Emitted Events

```json
{
  "index": 0,
  "guid": "4bf6f75d-69dd-4fd5-8137-95b802ca9028",
  "users": {
    "name": "alice",
    "age": 85
  }
}
```

## Example Configuration Options

Given the incoming Event below, emit all elements from the 'numbers' array as separate Events in a field called "number".

```json
{
  "numbers": [8, 13, 21]
}
```

```json
{
  "mode": "explode",
  "path": "<<numbers>>",
  "to": "number"
}
```

Given the incoming Event below, emit all elements from the 'people' array as separate Events in a field called "person".

```json
{
  "numbers": [8, 13, 21],
  "people": [
    {
      "name": "Alice",
      "job": "Engineer",
      "language": "English"
    },
    {
      "name": "Bob",
      "job": "Student",
      "language": "English"
    }
  ]
}
```

```json
{
  "mode": "explode",
  "path": "<<people>>",
  "to": "person"
}
```

##### Extract mode

Use regular expressions to extract text from fields in incoming events.

## Features

- Specify one or more [Ruby compatible](http://rubular.com/) regular expressions to match and extract text from incoming events.
- Any matched text will be included in an array in a new event. If there are no matches, no event will be emitted by default.
- Specify the name of the key for each array of matched text.

## Configuration Options

- `mode`: 'Extract'
- `matchers`: When using 'Extract' mode, define an array of regular expression configuration blocks to match text in incoming events:
- `path`: Specify the wrapped JSON path for the field containing text to extract.
- `regexp`: Specify the regular expression to be used to extract text.
- `to`: Specify the name of the field to contain the array of matches.
- `emit_no_match`: (Optional) By setting `emit_no_match` to `true` an event will also be emitted if the regular expression does not match.

## Emitted Events

```json
{
  "email_addresses": ["alice@example.com"],
  "ips": ["10.1.1.12"],
  "urls": ["http://example.com", "https://tines.com"]
}
```

## Example Configuration Options

Given the incoming event below, extract all email addresses and store them in a field called 'email_addresses' in a new Event.

```json
{
  "text": "You received to email to alice@example.com sent from bob@example.com"
}
```

```json
{
  "mode": "extract",
  "matchers": [
    {
      "path": "<<text>>",
      "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
      "to": "email_addresses"
    }
  ]
}
```

Given the incoming event below, extract all email addresses from 'text' store them in a field called 'email_addresses'; extract all URLs from 'referrers' and store them in a field called 'urls'; and extract all IP addresses from 'servers' and store them in a field called 'ips'.

```json
{
  "text": "You received to email to alice@example.com sent from bob@example.com",
  "metadata": {
    "servers": "10.1.1.1, 10.2.3.4, 10.15.6.8",
    "referrers": "https://tines.com and https://www.example.com"
  }
}
```

```json
{
  "mode": "extract",
  "matchers": [
    {
      "path": "<<text>>",
      "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
      "to": "email_addresses"
    },
    {
      "path": "<<metadata.referrers>>",
      "regexp": "https?:\\/\\/[\\S]+",
      "to": "urls"
    },
    {
      "path": "<<metadata.servers>>",
      "regexp": "\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b",
      "to": "ip_addresses"
    }
  ],
  "message": "Email addresses taken from text, Urls from referrers and IPs from servers."
}
```

##### Implode mode

Reassemble a previously exploded array into a single event.

For a demo of `explode` and `implode` in use in a story see [here](https://explained.tines.com/en/articles/7228858-using-explode-and-implode-in-tines).

## Features

- Collect individual events which have been emitted as part of an explode and store them in memory. When either all of, a specified number of, or after waiting a specified amount of time, the exploded events that have been collected will be emitted in a single event.
- Specify the data from incoming individual events to collect into the single event emitted by the current action.
- Specify a GUID to collect only related events together - usually the GUID emitted in events as part of an explode operation.
- Specify the number of events that the implode should collect - usually the size of an exploded array.
- Specify the amount of time that implode should collect events for before emitting an event. 

## Configuration Options

- `mode`: 'implode'
- `item_path`: The path to items to collect into an array in the output event.
- `guid_path`: The unique GUID used to identify exploded events.
- `size_path`: The number of events implode should collect before emitting an event.
- `seconds`: The number of seconds to wait after implode receives the first event before emitting an event. This can also be used in conjunction with `size_path`, resulting in an event being emitted as soon as either the `size_path` or `seconds` requirement is reached.

## Emitted Events

```json
{
  "implode": [
    {
      "receive_webhook": {
        "numbers": [1, 2, 3]
      },
      "explode_array": {
        "guid": "8bc687b5-764f-492e-9210-21ac502ffa98",
        "index": 0,
        "size": 3,
        "number": 1
      }
    },
    {
      "receive_webhook": {
        "numbers": [1, 2, 3]
      },
      "explode_array": {
        "guid": "8bc687b5-764f-492e-9210-21ac502ffa98",
        "index": 1,
        "size": 3,
        "number": 2
      }
    },
    {
      "receive_webhook": {
        "numbers": [1, 2, 3]
      },
      "explode_array": {
        "guid": "8bc687b5-764f-492e-9210-21ac502ffa98",
        "index": 2,
        "size": 3,
        "number": 3
      }
    }
  ]
}
```

## Example Configuration Options

Reassemble an array of 'exploded' URLs from a previous action in the story:

```json
{
  "mode": "implode",
  "item_path": "<<transform_url.new_url>>",
  "guid_path": "<<explode_urls.guid>>",
  "size_path": "<<explode_urls.size>>"
}
```

Reassemble split events from branches of a story which were running asynchronous:

```json
{
  "mode": "implode",
  "guid_path": "<<STORY_RUN_GUID()>>",
  "size_path": "3"
}
```

Consolidate API paging requests into a single event by setting the size to the number of pages available:

```json
{
  "mode": "implode",
  "item_path": "<<get_all_actions.body.results>>",
  "guid_path": "<<STORY_RUN_GUID()>>",
  "size_path": "<<get_all_actions.body.meta.pages>>"
}
```

Consolidate API paging requests into a single event after waiting five minutes:

```json

{
  "mode": "implode",
  "item_path": "<<get_all_actions.body.results>>",
  "guid_path": "<<STORY_RUN_GUID()>>",
  "seconds": 300
}
```

##### Message only mode

Produce an event with arbitrary structure, usually incorporating upstream data.

# Features

Emit an event with data of any type – an object, a list, text, etc. Hardcode data, or refer to upstream data using [formula expressions.](https://www.tines.com/docs/formulas/)

### Looping

You can also configure the action such that instead of producing a single event, it can produce an event based on each element of a list or object. Looping helps you to easily transform, filter or reduce incoming events.

- Specify the path to a field in an incoming Event that contains a list or an object and Tines will invoke the action for each element of the list or object.
- When specifying the output event payload, a `LOOP` object will be provided for each loop iteration. The `LOOP` object will contain:
  
  - `value` – The current value in the loop.
  - `index` – The current index in the loop.
  - `key` - When iterating over key/value pairs in an object, this is the current key in the loop. This will be absent when iterating over a list.
  - `previous_result` – The result of the previous iteration.
- A single output event will still be emitted.
- The payload of the output event will always be a list. It can potentially contain `NULL` elements.

##### Loop limits

- A loop can only be ran on a list or an object that contains fewer than `20,000` elements.
- If you wish to loop over a list or object that contains more than `20,000` elements, it is recommended that: 
  
  - The  [CHUNK_ARRAY](https://www.tines.com/docs/actions/formulas/functions/chunk-array) function is used to break the list into a list of smaller lists. 
  - An [Explode Mode](https://www.tines.com/docs/explode/) Event Transformation action is used to emit an event for each of the smaller lists.
  - The lists contained in each emitted event can be looped over without exceeding the loop size limit.
- Loops have a 5-minute processing timeout. If a loop exceeds this limit, processing stops and the action fails with an error. This prevents long-running loops from consuming excessive resources.

## Configuration Options

- `mode`: 'message_only'
- `payload`: a string or object which will be included in the emitted event. Only the `payload` contents will be included in the output event.
- `loop`: (Optional) Specify the name of a field in an incoming event that contains a list or an object.

## Example Configuration Options

Include a key and message object in the emitted event.

```json
{
  "mode": "message_only",
  "payload": {
    "message": "This is an automatically generated message from Tines",
    "another_message": "This is another message from Tines. The time is: <<DATE('now', '%Y-%m-%d %H:%M')>>."
  }
}
```

Include a simple message in the emitted event.

```json
{
  "mode": "message_only",
  "payload": "This is an automatically generated message from Tines"
}
```

Include structured data from previous actions.

```json
{
  "mode": "message_only",
  "payload": {
    "users": "=users",
    "logins": "=logins"
  }
}
```

### Looping

Given the incoming Event below, generate a message for each element in the list.

```json
{
  "numbers": [1, 2, 3]
}
```

```json
{
  "mode": "message_only",
  "loop": "=numbers",
  "payload": "This is message # <<LOOP.index>>, value=<<LOOP.value>>"
}
```

```json
[
  {
    "message": "This is message #0, value=1"
  },
  {
    "message": "This is message #1, value=2"
  },
  {
    "message": "This is message #2, value=3"
  }
]
```

Given the incoming Event below, generate a message for each element in the object.

```json
{
  "teams": { "team_1": "value_1", "team_2": "value_2" }
}
```

```json
{
  "mode": "message_only",
  "loop": "=teams",
  "payload": "This is message #<<LOOP.index>>, key=<<LOOP.key>>, value=<<LOOP.value>>"
}
```

```json
[
  {
    "message": "This is message #0, key=team_1, value=value_1"
  },
  {
    "message": "This is message #1, key=team_2, value=value_2"
  }
]
```

##### Throttle mode

Allow events to be emitted at a consistent, limited rate. Throttle mode helps you to respect rate limits when interacting with 3rd party APIs. This logic is applied to all events regardless of which story run they are from.

## Features

- Emit events at a limited rate and at consistent intervals.
- This logic is applied to all events across all story runs - separate story runs are not given separate capacity.
- The newly emitted event will also contain a `capacity_per_minute` key which shows how many runs of the action were allowed each minute when the event was emitted.
- Throttled events are placed in a queue until they are emitted. Events will be dropped instead of being added to the queue if there are 20,000 or more events in the queue already.

**Note:** The 20,000 throttled events queue is per action. A large queue for one action does not impact other actions.

## Configuration Options

- `interval`: the duration of time over which the throttle action schedules its runs. Allowed values are minute (default), hour, and day.
- `runs_per_interval`: The maximum number of times that the action can run in each interval. Runs will be evenly spaced out across each interval.
- `events_per_run`: The number of events emitted in each run (1-10).
- `throttle_key`** **(optional): Group events by interpolated values (e.g., `user_ID`) instead of processing them as one queue. When configured, events with the same key value will be throttled together. If left blank, all events will be throttled together. Each unique throttle key is allowed to process up to `events_per_run` events per interval.

For example, if there were a Throttle action configured with 20 runs per minute and five events per run:

![Interval: minute, Runs per interval: 20, Events per run: 5](https://www.datocms-assets.com/55802/1742900454-screenshot-2025-03-25-at-11-00-51.png)

Then, if it received at least 100 events per minute, we would expect it to emit five events every three seconds.

**

If the Throttle action received more than 100 events per minute on average, it would continue to steadily put out five events every three seconds in the order they were received, but a backlog would begin to amass. If it started receiving less than 100 events per minute, it would eventually consume that backlog and start putting out less than five events every three seconds, and possible none at all for some runs.

##### Automatic mode

Transform data based on guidance written in plain English.

Under the hood, this feature uses build-time AI to compose Python code based on the guidance and the input you provide. Once you save your changes, the code is locked in place. This means that when the action runs, only the code executes, and no AI is involved.

## Features

- Powerful transformations: author data transformations, with all the power of code, without needing to know how to *write* code.
- Self documenting: read plain English descriptions to understand the transformation, without needing to know how to *read *code.

## Configuration Options

- `mode`: 'Automatic'
- `input`: The data the action will operate on.
- `guidance`: A plain English description of how input should be transformed to output.

## Example Configuration Options

Given the incoming event below, generate a message for each element in the object.

```json
{
  "teams": { "team_1": { count: 5 }, "team_2": { count: 7 } }
}
```

```json
{
  "mode": "automatic",
  "input": "<<teams>>",
  "guidance": "Name of the team with the highest count"
}
```

```json
{
  "output": "team_2"
}
```

## Practical example

Let's say you had a Webhook action that was set up to listen for any GitHub events from your organization's repo, and you wanted to use an Automatic Event Transform action to extract the relevant info.

![](https://www.datocms-assets.com/55802/1722518594-screenshot-2024-08-01-at-14-22-50.png)

The GitHub event data is fairly large, with a lot of info we don't care about (truncated sample):

```json
{
  "github": {
    "body": {
      "action": "created",
      "issue": {
        "url": "https://api.github.com/repos/tines/tines/issues/XXXX",
        ...
        "title": "Turn on token bucket for sprinklr",
        "user": {
          "login": "tinesengineer",
          ...
        },
        "state": "closed",
        "body": "let's get more data",
        ...
      }
    }
  }
}
```

When you open the automatic Event Transform action editor, it will assume that last-received event is the sample input you're using to generate the python script. After you type your prompt and hit **Generate**, the Automatic Event Transform action will use the input data and the prompt to generate a python script.

![](https://www.datocms-assets.com/55802/1722519333-screenshot-2024-08-01-at-14-30-08.png)

It will also immediately run the generated script with the sample input data and show the output:

![](https://www.datocms-assets.com/55802/1722519366-screenshot-2024-08-01-at-14-30-13.png)

You can modify the prompt or the input and regenerate the script as many times as you want. When you're certain that the script will handle all of your use cases, you can click **Save** to lock the generated script in place. Any subsequent events that are passed into the Automatic Event Transform action will be transformed by this script.

At any time you can reopen the Automatic Event Transform action to modify the prompt and/or use new data to regenerate the script. You can also click **Copy as Run Script** in the bottom right to extract the script as a standalone Run Script action if you want to modify the script yourself.

#### Send to Story

The Send to Story action allows you to send data to a [sub-story](https://www.tines.com/docs/stories/send-to-story) where it can be further processed.

Sub-stories are special stories in Tines that can be used to perform a common action or set of actions. Use the Send to Story action to send data to an 'analyze URL' sub-story or a 'deprovision user' sub-story.

> **WARNING:** All actions in a sub-story must be connected. Sub-stories do not support separate, unconnected sets of actions. The sub-story's exit action(s) must be in the same connected flow as the entry action.

## Features

- Send event data to sub-stories where it can be processed.
- Select from a list of sub-stories or define a formula function to specify the story name or ID
- After selecting your sub-story, the action will populate with the inputs required to send to the sub-story
- If the send to story action does not send all required inputs to a sub-story, it will fail

**

## Configuration Options

- `story`: Select the story from the list of available sub-stories to call. Alternatively, you can provide the ID of the story to which data should be sent. Use the `STORY` formulas expression (`<<STORY.story_name>>`) to refer to your story of choice.
- `payload`: If no [inputs](https://www.tines.com/docs/stories/send-to-story/#send-to-story-inputs) are defined for the sub-story, provide data that should be sent to the sub_story via the payload.
- `send_payload_as_body`: (Optional) True by default. When set to false, the payload emitted by the action is not nested under a `body` key.
- `send_to_draft`: (Optional) False by default. When set to true, allows sending an event to the specified draft of a story with change control enabled
- `loop`: (Optional) Specify the name of a field in an incoming event that contains a list.

## Entry Action Events

An entry action's event payload will also include a non-configurable `meta` key, that will be sent from the Send To Story action containing metadata about the calling action and story:

- `event_id` - The event ID received by the Send to Story action in the calling story, if such event exists. 
- `action_id` - The ID of the Send to Story action in the calling story.
- `story_id` - The ID of the calling story.
- `team_id` - The team ID of the calling story.
- `story_run_guid` - The story run guid associated with the calling Send to Story action.
- `draft_id` - The ID of the calligng draft, if exists.
- `group_id` - The ID of the calling group, if exists.

![](https://www.datocms-assets.com/55802/1761042747-screenshot-2025-10-21-at-11-32-15.png)

## Emitted Events

Events emitted by the Send to Story action will contain information returned by the sub-story's exit action.

### Looping

You can also configure the action for array processing using the `LOOP` action option. Looping helps you to easily process an array and transform, filter or reduce incoming events.

The loop will be processed **serially** in order to preserve the ordering of the array for event output.

- Specify the path to a field in an incoming Event that contains a list  and Tines will invoke the action for each element of the list.
- When specifying the output event payload, a `LOOP` object will be provided for each loop iteration. The `LOOP` object will contain:
  
  - `value` – The current value in the loop.
  - `index` – The current index in the loop.
- A single output event will  be emitted to the Send to Story action containing an aggregate of the loop results for the array.
- The payload of the output event will always be a list. It can potentially contain `NULL` elements.
- The loop option is only available on a top level Send to Story action. It cannot be configured for a nested send to story.
- As with normal StS configuration the [sub-story](https://www.tines.com/docs/stories/send-to-story/#sub-stories) input action must be a webhook type action and the output action must be a message-only mode event transformation action.

##### Loop size limits

- A loop can only be ran on a list that contains fewer than `20,000` elements.
- If you wish to loop over a list that contains more than `20,000` elements, it is recommended that: 
  
  - The  [CHUNK_ARRAY](https://www.tines.com/docs/actions/formulas/functions/chunk-array) function is used to break the list into a list of smaller lists. 
  - An [Explode Mode](https://www.tines.com/docs/explode/) Event Transformation action is used to emit an event for each of the smaller lists.
  - The lists contained in each emitted event can be looped over without exceeding the loop size limit.

##### Loop error handling

- If one index in the array produces a failure, the loop processing is stopped and an error is logged / indicated to the user.
- Error path handling is required if it is necessary to proceed with loop processing so should be built into the story.

## Example Configuration Options

Send a single URL to a sub-story to analysis.

```json
{
  "story": "<<STORY.analyze_url>>",
  "payload": {
    "url": "http://evil-site.net"
  }
}
```

When resolving the story name the system first looks in the current team, then for any stories that are globally enabled for Send to Story.

Send the same URL for analysis, but this time use the ID of the story.

```json
{
  "story": 7,
  "payload": {
    "url": "http://evil-site.net"
  }
}
```

### Looping

Given the incoming StS below, process each element in the list and output an aggregated event result containing the payload message for each element in the list.

```json
{
  "numbers": [1, 2, 3]
}
```

```json
{
  "story": "=STORY.test_loop"
  "loop": "=numbers",
  "payload": "Message: This is index # <<LOOP.index>>, value=<<LOOP.value>>"
}
```

```json
[
    "message": "Message: This is index #0, value=1",
    "message": "Message: This is index #1, value=2",
    "message": "Message: This is index #2, value=3"
]
```

#### AI Agent

The AI agent action allows you to securely and privately run a large language model (LLM) at any point in your workflow. It supports two modes of operation: **task** and **chat**, enabling both behind-the-scenes automation and interactive user experiences.

AI agent action usage is based on a credits system: all tenants include a monthly credit allowance, and each execution of the action deducts credits. See more details on [credits and executions](https://explained.tines.com/en/articles/9369092-how-ai-usage-and-ai-credits-work-in-tines).

Learn more about how AI works in Tines at [Tines Explained](https://explained.tines.com/en/collections/9473753-ai-in-tines).

## **Features**

- Invoke an LLM on demand in your workflow
- AI models run inside Tines's infrastructure, with strong security and privacy guarantees.
- Choose from a variety of language models
  
  - Please note that the list of available models may vary depending on your tenant’s region.
- Support for the AI agent action to use tools
- Support for the AI to write and run code to analyse data
- Configure system-level instructions to guide the AI’s behavior
- Include image data to take advantage of Claude's vision capabilities
- Host a user-facing AI chat experience (Chat mode)
- Configure alerts for when the action's token usage exceeds a certain threshold

## **Modes**

### **Task Mode**

Task mode supports invoking a large language model either in a simple one-shot fashion (prompt → response) or as an **autonomous agent** that can use tools to complete tasks.

When tools are attached, the model engages in a self-directed reasoning loop, invoking tools as needed until a solution is reached.

### **Chat Mode**

Chat mode enables a conversational experience hosted on a Tines page.

End-users can interact with the AI agent action in real time. The chat continues until the AI achieves a defined objective specified in the system instructions, at which point the action completes and emits a final event.

Creating a well defined, clear goal for the AI agent to work towards will help it to understand when the chat should be ended.

You can also add an explicit line to your system instructions that tells the AI agent when the chat should be considered complete. For example:

"The goal is to make suggestions for how to improve someone's Jira issue write up. Once suggestions have been successfully shared or determined unnecessary, end the conversation."

Or:
"Your goal is to assist a user in resetting their device logins. Once you've reset the user's account, end the conversation."

#### File attachments

AI agent chat mode pages allow users to attach the following file types:

- Images: `PNG`, `JPEG`, `GIF`, `WEBP`
- Documents: `PDF`, `CSV`, `MD`, `TXT` , `DOC`, `DOCX`, `XLS`, `XLSX`

Attachment limits:

- Users can attach up to three files per message.
- Each attachment may be up to 4.5MB in size.

If you're using a provider other than the default Tines provider, supported file types may vary.

## Tools

Tools are functions the model can access to perform specialized tasks beyond its built-in capabilities.

There are four categories of tools you can attach to the AI agent action, supported in both task and chat mode.

If the output of a tool is greater than `50,000` tokens, the output is automatically truncated when sent back to the LLM. This can be disabled by disabling tool output truncation in the action settings.

![](https://www.datocms-assets.com/55802/1776424006-untitled.png)

### **Templates**

Tines provides pre-built public templates for popular products to save you time building out workflows.

These templates can be added and configured as tools for the AI agent action.

Additionally, you can add private, user-defined templates as tools.

See our [templates documentation](https://www.tines.com/docs/actions/templates/templates/) for more information.

### **Send to Story**

You can attach existing Tines Stories as tools using Send to Story, which lets the AI agent action pass data into another Story as input.

Note that these external Stories exist within a separate change control scope from the Story containing the AI agent action.

See our [Send to Story documentation](https://www.tines.com/docs/actions/types/send-to-story/) for more information.

#### Timeout settings

You can configure timeouts for Send-to-Story tools by adding the `Timeout Duration` option to the tool. With this option set, the agent will gracefully conclude the tool use if the underlying story did not complete in time. This option will override whatever timeout duration is set in the Send-to-Story settings.

### **Custom tools**

Custom tools allow builders to define and configure tools directly within the same Story as the AI agent action, keeping everything under a single change control scope.

This facilitates tasks that may not be covered by existing public templates or tasks that require multi-action workflows that reference dynamic input values.

### Internal tools

AI agents have built-in functionalities designed to enhance and extend the capabilities of an agent without additional configuration.

**Think**

A scratchpad that an agent can use to plan out its actions before taking them. Designed based on [Anthropic's research](https://anthropic.com/engineering/claude-think-tool).

**Code Analysis**

An ability to write and run code, allowing the agent to perform data analysis and generate charts

### MCP

Tines supports connecting to remote MCP (Model Context Protocol) servers, allowing the AI agent action to call tools exposed by those servers. This enables integration with external systems and services that implement the MCP specification.

#### **Supported transports**

We currently support the following MCP transports:

- Streamable HTTP
- Plain HTTP

> Note: **SSE** and **stdio** transports are *not supported* at this time.

#### Supported authentication methods

- Bearer and generic header authentication
- OAuth 2.1 (authorization code flow with PKCE)

#### OAuth and client registration

Remote MCP servers vary in how they handle OAuth client registration.

- If the server supports **Dynamic Client Registration (DCR)**, Tines can automatically register an OAuth client during setup.
- If the server does **not** support DCR, you must manually create an OAuth application in the third-party platform and provide the resulting credentials to Tines.

In these cases, you will need to:

1. Create an OAuth application (e.g. a bot or integration app) in the third-party platform.
2. Configure the required redirect URI and scopes.
3. Copy the Client ID and Client Secret.
4. Create an OAuth credential in Tines using those values.
5. Reference that credential in the MCP connection record.

Once configured, the connection will function normally.

#### **Supported features**

When connecting to MCP servers, only Tools are supported.

We do not support:

- Resources
- Prompts
- Notifications

#### **Using MCP tools**

Once connected to a remote MCP server, any tools it exposes will appear alongside your other configured tools in the AI agent action. These tools can then be invoked by the agent in the same way as Send to Story, Template, or Custom tools.

## Configuration options

- `System instructions:` Optional system-level instructions that define the rules, context, and constraints for the AI. In **Chat** mode, this should define the bot’s goal; the chat ends when the goal is achieved.
- `Tools`: A list of tools the AI agent action can use. When provided, the AI acts autonomously to decide if/when to use each tool.
- `Output schema`: A JSON schema to validate the output structure.
- `Model`: The LLM to use (default: the default **smart** model).
  
  - When no tools are attached and no model is explicitly specified, the Al
    
    action defaults to the tenant's configured **fast** model. This can be changed
    
    on your tenant's Al settings page (`/settings/ai`).
- `Additional request parameters`: (Custom providers only) define key-value pairs that merge into the provider request at invocation time

### **Task mode options**

- `Prompt`: The input to send to the model. Can include input data, examples, or instructions.
- `Image`: (Claude models only) Base64-encoded image content, or an array of images.
- `Temperature`: Controls the creativity of the output. Range: 0–1 (default: 0.2).
- `Timeout`: Timeout for the LLM request in seconds (default: 30).
- `Retries`: Max number of retries (default: 25).

### **Chat mode options**

- `URL identifier`: Defines the public URL for the chat page.
- `Access control`: Configuration for who can access the chat.
- Theming options: Customize the chat page’s appearance:
  
  - `Action color`: Primary UI color
  - `Page logo`: Image to display
  - `Appearance`: "light" or "dark" mode
- `Initial message`: Message displayed to the user when the chat starts.
- `Idle timeout (minutes`): Automatically end the chat after a certain number of minutes without user activity.
  
  - Idle timeout only applies to chats where the user has sent at least one message.
  - The timer resets whenever the user begins typing a message. A warning is displayed when fewer than 30 seconds remain before the chat ends automatically.
  - When a chat times out, the `outcome` field under `meta` in the event output will be `idle_timeout`. This allows you to handle timeouts in your story run.

## Limits

<table border="1" height="407" style="border-collapse: collapse;"><tbody><tr><td>Limit</td><td>Description</td><td>Value</td></tr><tr><td><span>Max input tokens per minute per</span><span><span>&nbsp;</span>tenant</span></td><td><p class="p1">Input tokens allowed per minute per tenant.</p></td><td><span>900,000 (eu-west-1) or&nbsp;1,500,000 (other regions)</span></td></tr><tr><td><span>Max runs per minute</span><span><span>&nbsp;</span>per tenant</span></td><td><p class="p1">AI action runs allowed per tenant per minute.</p></td><td><span>40 (eu-west-1) or 100 (other regions)</span></td></tr><tr><td><span>AI provider max runs per minute per tenant</span></td><td><p class="p1">Runs to external AI providers allowed per tenant per minute.</p></td><td><span>500</span></td></tr></tbody></table>

## Emitted event

When in task mode with no tools attached, a single event is emitted each time the action runs. This event contains output from the AI model.

```json
{
  "output": "Estimated severity: high"
}
```

If the model returns valid JSON, or you have an output schema configured, Tines will automatically parse this in the event data:

```json
{
  "output": {
    "estimated_severity": "high"
  }
}
```

When using chat or task mode with tools, the event payload will also include the full conversation steps. For example:

```json
{
  "steps": [
    {
      "role": "user",
      "text": "How many users are on this tenant?"
    },
    {
      "role": "agent",
      "text": "I'll help you find out how many users are on this tenant by retrieving the user list"
    },
    {
      "role": "tool",
      "tool_name": "think",
      "inputs": {
        "input": "The user wants to know the total number of users on this tenant. I should use the list_users function to get this information."
      },
      "output": "Thought logged"
    }
  ]
}
```

Tines will include a `metadata` field in the event payload that specifies the model used, input and output token counts, credits consumed, duration elapsed in seconds, and remaining credits.

```json
{
  "input_tokens": 1943,
  "output_tokens": 417,
  "credits_used": 3,
  "remaining_credits": 4759,
  "model": "eu.anthropic.claude-3-7-sonnet-20250219-v1:0",
  "duration": 266.20275563
}
```

## Example configuration options

Proposing remediation steps for a security alert (`alert_data`):

```json
{
  "prompt": "Summarize the potential remediation steps for the alert\n\n Data:<<alert_data>>"
}
```

Analyzing a support request from a user (`support_request`):

```json
{
  "prompt": "Help the user with their product issue.\n\n Data:<<support_request.query>>",
  "image": "=support_request.screenshot.contents"
}
```

A security remediation agent.

```json
{
  "mode": "task",
  "prompt": "Remediate the following alert based on the step defined below:\n\n<alert>\n```\n<<cases_action.body.case>>\n```\n</alert>\n\n<remediation_task>\n<<cases_action.body.button.label>>\n</remediation_task>",
  "instructions": "You are a Security Remediation Agent responsible for executing remediation actions for security alerts.\n\nYour primary responsibilities are:\n\n1. Carefully analyze the remediation instructions provided in security alerts\n2. Execute the specified remediation actions using the appropriate tools available to you\n3. Report back on the outcome of each remediation action to the originating case using a Case Note\n4. Delete the \"Case Action\" that triggered the alert. This can be found by its ID.\n\nWhen receiving remediation instructions:\n- Parse the instruction carefully (e.g., \"Block URL xyz\", \"Disable user account abc\")\n- Identify the specific action required and the target of that action\n- Select the appropriate tool to execute the requested action\n- Execute the action precisely as instructed\n- Document all steps taken during remediation\n- Report success or failure of the remediation action\n- Include any relevant details or errors encountered during remediation\n\nAvailable remediation actions may include:\n- Blocking URLs/domains\n- Disabling user accounts\n- Revoking access tokens\n- Isolating endpoints\n- Adding IPs to blocklists\n- Removing malicious files\n- Resetting credentials\n\nAfter completing the remediation action, always:\n1. Verify the action was successful\n2. Create a detailed Case Note on the originating case with:\n   - The action that was taken\n   - The outcome (success/failure)\n   - Any relevant details or metrics\n   - Timestamp of when the action was completed\n   - Any recommended follow-up actions\n3. Delete the \"Case Action\" that triggered the alert. This can be found by its ID.\n\nNever deviate from the explicit remediation instructions provided. If instructions are unclear or appear potentially harmful, request clarification before proceeding.\n\nUse the following example for formatting:\n```\n## Remediation Action Completed: Block Sender Domain\n\nCompleted the remediation action to block the sender domain **suspicious.ru**.\n\n### Actions Taken:\n1. ✅ Successfully added the domain to the Sublime Security suspicious email addresses list\n2. ❌ Attempted to add the domain to CrowdStrike IOC blocklist but received an error: \"No IOCs could be identified\"\n\n### Recommendation:\nConsider manually adding this domain to your email gateway blocklist to ensure complete protection.\n\n"
}
```

### Chat mode

A support assistant that assists users troubleshoot IT issues.

```json
{
  "mode": "chat",
  "instructions": "You are a helpful assistant that helps users troubleshoot IT issues.",
  "initial_message": "Hi, how can I help you today?",
  "visibility": "tenant",
  "pages_appearance_mode": "light",
  "pages_action_color": "#777FFF",
  "page_mode": "success_page",
  "page_logo": null,
  "url_identifier": "6922c10ca8fe089ad0706eb31cb3a4de",
  "idle_timeout_minutes": 15
}
```

## Monitoring Alerts

You can configure two types of usage alerts from the AI agent action's status panel:

![](https://www.datocms-assets.com/55802/1756455274-screenshot-2025-08-29-at-09-13-29.png)

*Token usage thresholds can be configured for a particular period*

- **Notify:** When the AI agent uses enough tokens to exceed the threshold for the selected period (Daily, Weekly, Monthly, All time), the recipients of the story will be notified.
- **Disable action:** Will notify the recipients of the story when the threshold is exceeded, but will also disable the action to prevent further token usage.

## Slack App Configuration

To configure a Slack app for your AI agent action, go to the **"Slack app"** section of your agent's build panel and click **"connect"**, then follow the on-screen instructions to create a new Slack app in your workspace and connect it to your agent. Note that only AI agent actions in **chat** mode can be connected to Slack.

![](https://www.datocms-assets.com/55802/1762165026-slack-app-section.png)

Once connected, you can test the connection by initiating a conversation with the agent in Slack (see [this](https://slack.com/intl/en-gb/help/articles/33076000248851-Understand-AI-apps-in-Slack) article from Slack on the different ways to interact with agents in your workspace). 

If you are connecting an agent as part of a new [change control](https://www.tines.com/docs/change-control/) draft, you will need to perform an extra step if you wish to test the connection before setting it live. Within the **"Event Subscriptions"** section of your Slack app's settings, you will need to append `?draft=DRAFT_NAME` to the Request URL. 

**

Once your draft has been merged into the live story, you should return to this setting's page and remove the `?draft=DRAFT_NAME` part from the Request URL.

**Please note**: to avoid depleting your tenant-wide credits, the AI agent action type is not available in your personal teams.

### Templates

#### Public Templates

While you can easily create any action from scratch, Tines also provides pre-built templates for commonly used products to save you time while building your workflows.  For example, our templates support creating tickets, disabling user accounts and performing threat intelligence scans.

## Public templates

Action templates available to all customers are categorized by product. Templates can be added to a story by dragging the template relevant to the task onto the storyboard. 

**

### Required and optional inputs

All required input fields for a template will be displayed when you select the task with a `Required` denotation. You can additionally add optional fields by selecting `+ Optional Inputs` at the bottom of the build panel. If you would like to remove an optional input, select the `-` symbol next to that input.

### View template configuration

To view the configuration of a template, select `Look inside` from the kebab menu next to the product name in the builder panel. This will display the http request configuration being used by the template. Pill values that contain `INPUT.<text>` will reference the values you input to the template. 

### My desired task doesn't exist

If the task you are searching for does not exist in the product's list of tasks, you can select our lightweight template `Build your own` that will assist you in building your own action. This template is pre-configured with the template's base URL, relevant authorization headers, and a link to that product's API documentation, which provides the base for customizing the action to your desired output. Additionally, you can make use of our [cURL to Tines functionality](https://www.tines.com/docs/actions/shortcuts#import-from-curl) if the product's API docs have cURL commands.  Please let us know if you would like a task added for a product, we are always looking to improve this list to meet your needs!

### Template search shortcut

If you know the exact template you would like to use, search for your template within the storyboard command palette via `⌘K`. Once you have identified your template, select the template for it to appear on the storyboard.

**

#### Private Templates

If you have a private API that you use internally, or if you have custom fields and configurations for your own tools (like Jira, Splunk, AWS etc.) you can create your own private templates within Tines.

## Overview

Templates are **team-scoped** — every template is owned by a team and is, by default, only visible to members of that team. You can expand access by sharing templates with specific teams or making them available to all teams on the tenant. Admins can view all templates across the tenant from the **Templates** page in tenant settings.

## Creating private templates

To create a private template, select any action from any story and then select **Create template** from the **more** dropdown to open the template editor. The editor will autofill any information from the selected template and is broken up into four sections: **Details**, **Action inputs**, **Action options** and **Test**.

![](https://www.datocms-assets.com/55802/1730280749-screenshot-2024-10-30-at-09-32-25.png)

### Details

![](https://www.datocms-assets.com/55802/1730281098-screenshot-2024-10-30-at-09-38-11.png)

This section has a few key details for the template. **Name** and **product** are required while **Description** is optional.

### Action inputs

![](https://www.datocms-assets.com/55802/1730284724-screenshot-2024-10-30-at-10-38-42.png)

> **NOTE:** Not supported for group actions

> **IMPORTANT:** When creating private templates for AI agents and Workbench, you must add at least one action input field. You must also reference this field within action options.

Action inputs provide a simple interface to users, hiding the template's complexity. Input types include:

- Text: a string of text (can include formula pills)
- Object: more complex objects (including formula pills). A JSON schema can be provided to define the format of the object, which is used by Workbench to ensure that data is passed in properly.
- Boolean: true/false (can be replaced with a formula)
- Code: a block of code (supported languages include HTML, Python, SQL, XML and GraphQL)
- Date: a date than can be selected from a calendar pop-up (can be replaced with a formula)
- Number: String representing a number (can be replaced with a formula)
- Option: a list of options to be selected from a dropdown, or from a list of checkboxes in multi select mode (can be replaced with a formula)
- Credential: a special input type that allows a template to access user-provided credentials in a secure manner. A template can't have more than one credential input.

![](https://www.datocms-assets.com/55802/1730290674-screenshot-2024-10-30-at-12-17-27.png)

While some input types have unique configuration options (those described above), there are a few that almost all inputs share:

- Path: located in the header, the path to access this value in the action options. If the input name is "Client number" then the path would be `INPUT.client_number`.  Click to copy.
- Name: update the name of the input.
- Helper text: text for a tooltip description of the input that's visible to the user. (All except credential.)
- Required: ensures that the user sees the input when they drag out the template. Non-required fields will be hidden behind an **Optional fields** button initially. (All except credential.)
- Default value: set the default value for the input. (All except credential.)
- Delete: delete the input. (Bin icon.)

Inputs can also be reordered by dragging.

### Action options

![](https://www.datocms-assets.com/55802/1730285317-screenshot-2024-10-30-at-10-48-14.png)

Action options configure the template's underlying action. The options provided must be valid for the action type, otherwise the template will not save. Configuration works exactly the same as it does for [actions on the storyboard](https://www.tines.com/docs/actions/).

#### Accessing input values

To access an action input value inside the action options, you need to use the special `INPUT` key. `INPUT` is an object where every key/value pair is the input and its value. You can find the path for a given input in the action input editor popup.

You can also access [credential metadata](https://www.tines.com/docs/credential-metadata/) and any other information that you could access through the [info formula key](https://www.tines.com/docs/formulas/referencing-data/#info) (`INFO.credential.<credential_name>`) path through the `INPUT` key. The path pattern is `INPUT.<input_name>.info`. For instance, if you have a credential input called "Jira credential" and you want to access the "domain" metadata variable, you could access it with `INPUT.jira_credential.info.metadata.domain`.

### Test

![](https://www.datocms-assets.com/55802/1730287856-screenshot-2024-10-30-at-11-30-54.png)

> **NOTE:** Not supported for Group actions.

The test section allows you to test out your inputs and options on the fly by clicking the test button. Action inputs are available and can be edited in this view but action options are hidden, just like they will be for users. Return data or errors will be printed in a JSON blob below the test button.

### Access

The **Access** tab controls which teams can see and use the template. There are three access levels:

- **Team only** *(default)*: The template is only visible to members of the owning team.
- **Specific teams**: Share the template with one or more specific teams. Select the teams you want to grant access to from the list of available teams.
- **All teams**: The template is visible to all teams on the tenant.

You can also choose to include **team members' personal teams** when sharing, which makes the template available in those members' personal team contexts as well.

> **NOTE:** Changing a template's access level requires the Manage templates permission on the owning team.

## Editing private templates

To view existing Private Templates, navigate to the Templates page inside the team section, from the top-left menu. There you'll find all the templates belonging to that team. Select a template to access all the configuration described above.

## Using private templates

![](https://www.datocms-assets.com/55802/1776432463-screenshot-2026-04-17-at-14-27-15.png)

Private templates can be accessed through the templates drawer on the storyboard, just like [public templates](https://www.tines.com/docs/templates/). Click the HTTP action to open the templates drawer and select the **Private templates** tab at the top of the drawer. Private templates are grouped by product, just like public templates. After clicking into the product, you can drag the private template onto the storyboard.

# Private templates via Story copilot

Story copilot can manage your tenant’s private template library directly from the chat. In **build** mode, you can ask story copilot to:

- **Browse and search** your team’s private templates by name, description, vendor, or product.
- **Create templates** from scratch or from existing actions on the storyboard, including parameterized inputs for reusable configuration.
- **Update templates** - modify names, descriptions, options, inputs, or sharing settings. Optionally replace storyboard actions that use the template in one step.
- **Delete or move templates** between teams.
- **Control access** - set templates to team-only, global, or shared with specific teams.
- **Bulk operations** - create, update, or delete up to 100 templates in a single request. Useful when migrating an integration library, onboarding a new vendor, or cleaning up deprecated templates.

In **ask** mode, story copilot can search and read templates to help you understand what’s available or plan changes without modifying anything.

You can also upload API specs (OpenAPI/Swagger), JSON, or YAML files and story copilot will parse and navigate them section by section, enabling workflows like “here’s our internal API spec, create templates for each endpoint” without pasting content into the chat manually.

> **TIP:** File uploads must not exceed the token limit. If your file is too large, split it into smaller chunks before uploading.

#### MCP server

[Model Context Protocol](https://modelcontextprotocol.io/about) (MCP) is a standard protocol for LLMs to connect to external services and interact with them. You can build custom remote MCP servers directly from the Tines storyboard.

To get started, search for "MCP" in the Templates section on the left panel, and drag your first MCP server to the storyboard. After configuring it with some tools, you can reference the server URL and authentication parameters from your MCP client to try it out.

### **Adding tools**

MCP servers support template, send-to-story and custom tools. They can can be configured in the [same way as the AI Agent action.](https://www.tines.com/docs/actions/types/ai-agent/) They will be exposed directly via the server and available for invocation from your client. Tool names, descriptions and arguments are automatically available on your MCP client after successful connection. 

### Access control

There are four access control modes for MCP servers:

- Anyone with the path (public)
- Anyone with the secret (passed in via the URL or HTTP Authorization)
- With a [Tines API Key](https://www.tines.com/api/authentication/)
  
  - Members of specified teams
  - Any member of the Tines tenant
- With OAuth
  
  - Members of specified teams
  - Any member of the Tines tenant

You can read more about these [here](/docs/actions/types/webhook/#authentication).

### Configuring your MCP client

An MCP server created in Tines can be used directly by any client that supports the Streamable HTTP protocol for remote MCP servers. Typically, you can use an MCP setting like this on a client that supports this syntax.

```json
{
  "mcpServers": {
    "tines": {
      "url": "https://<tenant>.tines.com/mcp/<mcp-path>",
      "headers": {
        "Authorization": "Bearer <webhook-secret or api-key>"
      }
    }
  }
}
```

Additionally, if you are using a client that only support local servers (stdio), you can use [mcp-remote](https://www.npmjs.com/package/mcp-remote) to act as a local proxy. To do this, you need [Node.js](https://nodejs.org/en/download) installed locally and the following MCP settings:

```json
{
  "mcpServers": {
    "tines": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://<tenant>.tines.com/mcp/<mcp-path>",
        "--header",
        "Authorization:${AUTH_HEADER}",
        "--transport",
        "http-only"
      ],
      "env": {
        "AUTH_HEADER": "Basic <webhook-secret or api-key>"
      }
    }
  }
}
```

> **TIP:** You can test your MCP server using the [MCP inspector](https://modelcontextprotocol.io/legacy/tools/inspector). To do this, run npx@modelcontextprotocol/inspector in your terminal. Then, in the browser, set the server URL and configure it for "API Token Authentication".

### Protocol details

- Only Streamable HTTP is supported (no SSE or stdio). See above for information about using a `mcp-remote` as an adaptor
- Authentication can be done via the HTTP `Authorization` header or via the URL (when using secrets)
- The supported MCP protocol version is`2025-06-18` 
  
  - Version `2025-03-26` has partial support, and should work on the majority of clients that support it.
  - Version `2024-11-05` is not supported
- Only tools are supported. Tool responses are always text.

##### Unsupported features

- Prompts
- Resources
- OAuth
- Notifications
- Sessions
- Pagination
- JSON-RPC Batching (only relevant for spec `2025-03-26`)

### Limits

The MCP server implementation is subject to the same [limits that apply to response-enabled webhooks](https://www.tines.com/docs/actions/types/webhook/#response-enabled-webhooks). That is:

- Tool response time cannot exceed 30 seconds.
  
  - After this time your client will receive a timeout error.
  - When this happens, the tool can continue to run in the background, but the result does not reach the client.
- You cannot have more than 100 (1000 in dedicated tenants) concurrent tool/call requests.
  
  - These are counted globally across all MCP servers and response-enabled webhooks in your tenant.
- Tool execution happens asynchronously and is subject to the same [fair orchestration system](https://explained.tines.com/en/articles/9887588-what-is-action-run-orchestration) that applies to all stories.

### Emitted events

The MCP server will emit events for the requests it gets. These are:

- `initialize`
- `tools/list`
- `tools/call`

In the case of the `tools/call` emitted event, the tool result is not included in the event. However, each tool call will be available on the tool itself, which emits an event upon completion.

To avoid unnecessary noise, methods that do not provide meaningful information (such as `notifications` or `ping`) will not result in downstream events.

When an API key is used to authenticate, the email of the API key's owner will be available in the downstream event, at `<<headers.email>>`.

### Other configuration options

The description of the MCP server action will be exposed directly to clients as `instructions`, in case you wish to share general context about the MCP server with the LLM using it.

Each tool in an MCP server can include optional [annotations](https://modelcontextprotocol.io/specification/2025-11-25/schema#toolannotations) that describe its behavior to MCP clients. These hints help clients make decisions about how and when to invoke tools.

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 31.2678%;"><col style="width: 46.4374%;"><col style="width: 22.2899%;"></colgroup><tbody><tr><td>Hint</td><td>Description</td><td>Default</td></tr><tr><td>Read only</td><td><div><div>This tool only reads data and does not modify anything. Overrides the destructive and idempotent hints.</div></div></td><td>Off</td></tr><tr><td>Destructive</td><td><div><div>This tool may permanently delete or modify data. Ignored when read only is on.</div></div></td><td>On</td></tr><tr><td>Idempotent</td><td><div><div>Calling this tool repeatedly with the same arguments has no additional effect.</div></div></td><td>Off</td></tr><tr><td>Open world</td><td><div><div>This tool may interact with external entities not described by its arguments.</div></div></td><td>On</td></tr></tbody></table>

To configure hints, expand the Tool hints section below each tool's configuration. 

![](https://www.datocms-assets.com/55802/1775148215-tool-hints.png)

MCP servers share many of the [configuration options that webhooks offer](https://www.tines.com/docs/actions/types/webhook/#configuration-options). For example, you can set these like you would on a normal webhook:

- Enable CORS
- Match rules
- Rate limiting

### Tools

Tools are elements that can be dragged onto your storyboard outside of our 8 action types. 

These include the following options: 

1. [Page](https://www.tines.com/docs/pages)
2. [Record](https://www.tines.com/docs/records-cases/records)
3. [Run Script](https://www.tines.com/docs/run-script/)
4. [Case](https://www.tines.com/docs/records-cases/cases/creating-a-case#create-from-storyboard)
5. [Group](https://www.tines.com/docs/stories/groups)
6. [Note](https://www.tines.com/docs/stories/notes/)
7. [Sections](https://www.tines.com/docs/stories/sections/)

#### Record

#### Run script

## Using the action

This action is available from the Tools menu as show in the below screenshot.

![](https://www.datocms-assets.com/55802/1741360467-screenshot-2025-03-07-at-15-14-19.png)

Let's go into detail about the fields on Run Script.

### Script 

This is where to place the Python code you would like to execute.

If there are any dependencies to import for your script, declare them first as you normally would.

Define your main function and pass any inputs by using `def main(input):` and then write your script.

**Important: you must define a **`**main**`** function in your script.**

Define your return in the format of a JSON object. This will be the content of the event that gets emitted from the action. For example, this is the output of the sample script in the template.

![run-python-script-event-detail](https://www.datocms-assets.com/55802/1714475584-runpythonscripteventdetail.avif)

*Output of script*

### Inputs 

These are the values that will be passed to your script at runtime. You can use [pills](https://www.tines.com/docs/formulas#formulas-pills-inside-text-fields-tags-vs-values) in the builder to refer to any data from other actions upstream in your story.

To refer to these values in your script by calling `input["<<object_name>>"]`.

In the template action, note that **Input** has a key named `array_length`. So, in the Script section, this is called with `input["array_length"]`.

### Requirements 

List the dependencies required to run your script in the format of a requirements.txt file with each requirement separated by a new line. Example:

```python
numpy==1.25
http==0.02
```

### Timeout 

Time in seconds to wait before terminating the script. The default is 10 and the maximum is 110.

### Networking mode

Since we are making requests from AWS Lambda, egress IPs are different than the ones the tenant uses (e.g. to make HTTP requests from the HTTP Request action). We support three networking modes:

- `Standard`: the egress IP address is subject to change and shared with other customers.
- `Dedicated`: the IP addresses are static and can be found for your tenant by visiting `<<tenant-domain>>/info`, under `lambda_egress_ips`. These IP addresses are shared with other customers unless you are using a dedicated tenant.
- `No networking`: the script will not have internet connecitivty.

### AWS Support

With Run Script you can utilize Python scripts with the Python AWS SDK (`boto3`) or the AWS CLI to interact with AWS services. Before you begin, you will need to attach a Tines AWS Credential to your Run Script with the required permissions.

### AWS IAM Role

You can start by creating an AWS IAM role which should have permissions to execute AWS API calls on your behalf and should be assumable by an AWS Lambda function from Tines AWS account.

To get started, follow please [this doc](https://www.tines.com/docs/credentials/aws) to setup a Tines AWS Credential. Once you have set it up, you will then need to make sure that the Trust Relationship for the IAM Role in your AWS account matches the following, where `TinesAwsCredentialGeneratedID` is the External ID from the Tines AWS Credential you created. We recommend allowing assume role requests from both our AWS accounts.

```javascript
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "857223745291"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "TinesAwsCredentialGeneratedID"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "825838939522"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "TinesAwsCredentialGeneratedID"
        }
      }
    }
  ]
}

```

After this you can attach any permission policy you wish to for the AWS API calls you will be making from the Run Script action. Once you have added the IAM role ARN of the newly created IAM in Tines AWS Credential, you can then reference the same Credential in the Run Script Action like the following image.

![](https://www.datocms-assets.com/55802/1716228376-preview.png)

### AWS Python SDK Support (`boto3`)

You can now start using Boto3 to interact with AWS APIs securely. The necessary credentials, retrieved from the IAM role you specified, will be included in the `input` event of your function under `input['AwsCredentials']`. Below is a example Python function example that lists S3 buckets.

```python
import boto3

def main(input):
    # Return early if credential fetch failed
    if not input.get('AwsCredentials', {}).get('Success', False):
        return input
    creds = input["AwsCredentials"]
    # Use the assumed credentials to access S3 in the other account
    s3 = boto3.client(
        's3',
        aws_access_key_id=creds['AccessKeyId'],
        aws_secret_access_key=creds['SecretAccessKey'],
        aws_session_token=creds['SessionToken'],
    )

    # List buckets
    buckets = s3.list_buckets()
    return {'buckets': [bucket['Name'] for bucket in buckets['Buckets']]}
```

### AWS CLI Support 

Alternatively, you can also use directly use the `AWS CLI` Runtime in your Run Script action and use `aws` cli to interact with AWS services. Example:

```bash
aws sts get-caller-identity
```

![](https://www.datocms-assets.com/55802/1724771166-cropped-aws-cli.png)

## Python in Tines: How it works

Tines runs in AWS, and we make creative use of their underlying services so that we do not actually process any Python in the application. This is great for multiple reasons, but here are the top 2:

1. Your code is executed in a secure, regulated environment, running in the same region as your tenant.
2. We did not have to update (and will not have to maintain) any of our code to support running Python in our Ruby-based platform. Granted, that is a selfish reason, but it means we get to spend more cycles building out more amazing features.

### Behind the scenes 

Your cloud tenant has a role that allows it to create or invoke Lambdas in our AWS environment, adjacent to your tenant. Each Lambda is only accessible by the tenant that created it.

The Lambda itself has a limited role assigned, so your Lambda has zero access to any other AWS resources.

Any packages in the Requirements will be built at runtime as Lambda Layers.

On the first run of your Python script action, you may notice a short delay while your Tines tenant dynamically builds the Lambda function required. Any subsequent requests should respond promptly.

## Limitations

- Run Script Action is currently limited to the `python3.13` runtime.
- Run Script Action is available for self-hosted but there may be some configuration needed - for more details, see [Run script for self-hosted](https://www.tines.com/docs/self-hosting/additional-applications/run-script-for-self-hosted/).
- As we are using AWS Lambda under the hood for `mode: "cloud"`, we are subject to their quotas on script execution. Specifically: 
  
  - Requirements have a package limit of `250MB` in unzipped size
  - The maximum payload you can pass to the function is 6 MB (including the size of the code).
  - The maximum output of the function is 6 MB.
  - The disk space available for ephemeral storage is limited to `756MB`.
  - Please refer to the [AWS docs](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html) for more details.

## Best practices 

1. Pull the script from your code repository. While Tines has version control and change management for your stories, we are an automation platform, not a code repository. When dealing with scripts, it is best practice to keep your code in a managed repository. Luckily, Tines makes it easy to call out to your repository of choice so you can maintain proper code hygiene and integrate your script with the rest of your Tines story. 
2. Make your scripts idempotent if possible. While we attempt to run script actions exactly once, they may need to be retried. This means that if the function has side-effects (e.g. making a call to an external service), those could occur more than once.
3. Depending on the nature of your scripts, you may not want all members of your Tines tenant to have direct access to them. In that case, we recommend using a separate [Tines team](https://www.tines.com/docs/admin/teams) to create a more isolated environment where only certain users have access. You can then configure any of the script workflows as [send to stories](https://www.tines.com/docs/send-to-story/). This would allow other teams in your tenant to call the send to story to execute a script and receive the output, but not permit access to the actual script itself.



## Frequently asked questions and troubleshooting

- [Can I remove the sixty-second timeout when executing a Python script in Tines?](https://explained.tines.com/en/articles/8100310-can-i-remove-the-sixty-second-timeout-when-executing-a-python-script-in-tines)
- [Script error: "The script payload is too large to be processed"](https://explained.tines.com/en/articles/9134080-tines-troubleshooting-script-error-the-script-payload-is-too-large-to-be-processed)

##### Run Script over Tunnel

You can use [Tines Tunnel](https://www.tines.com/docs/tunnel/) to connect your [Run Script](https://www.tines.com/docs/run-script/) actions to the tines-command-runner container running on your self-hosted platform. This way, your Python code runs within your environment, can access resources in your network, and you can install custom packages.

> **TIP:** This is an alternative to [Command-over-HTTP](/docs/self-hosting/additional-applications/command-over-http/) for Python via Tunnel which provides more flexibility and isolation.

To get started, you will need:

- A Tines Tunnel with a running `tines-tunnel` container in your network.
- A running `tines-command-runner` container 
- The `tines-tunnel` container must be able to reach `tines-command-runner`

Once this is set up, you can configure a Run Script action to use:

> **Implementation for Run Script**: `Docker`
> **Use tunnel**: `<your tunnel>`
> **Command Runner URL (optional)**: URL where the tines-command-runner container can be accessed from the perspective of the tunnel container.

![](https://www.datocms-assets.com/55802/1742237225-screenshot-2025-03-17-at-2-46-33-pm.png)

> **TIP:** You won't be able to find the use tunnel option until you've selected Docker as the Implementation for Run Script. This is because tunnels cannot be used with Run script on cloud.

## Example via `docker run`:

```bash
docker network create tines-net

docker run -d --name tines-command-runner -p 4400:4400 --network tines-net   tines/tines-command-runner:latest

docker run -d --name tines-tunnel --env TINES_TUNNEL_SECRET="..." --network tines-net tines/tines-tunnel:latest
```

You can also combine the [tines-tunnel docker-compose file](/docs/admin/tunnel/#deploying-tunnel) with the [tines-command-runner docker compose file](https://www.tines.com/docs/self-hosted/deploying-tines/docker-compose/run-script-setup/#docker-compose-configuration).

##### Custom runtimes

## Overview

Custom runtimes allow you to upload your own Python runtime environment to run scripts in Cloud with full control over dependencies and Python versions. This gives you flexibility beyond the standard python3.9 and python3.13 runtimes, allowing you to bundle specific package versions, system libraries, or even custom Python builds.

> **NOTE:**
> ### Language support
> 
> Currently, custom runtimes only support Python. Other languages are not supported yet.
> 
> ###   
> ZIP size limits
> 
> <table border="1" height="322" style="border-collapse: collapse;"><tbody><tr><td>Limit</td><td>Value</td></tr><tr><td><span>Maximum uncompressed size</span></td><td><span>1 GB</span></td></tr><tr><td><span>Maximum file</span><span>&nbsp;size per</span><span>&nbsp;file</span></td><td><span>25</span><span>0 MB</span></td></tr><tr><td><span>Maximum ZIP</span><span>&nbsp;entries</span></td><td><span>100,000</span></td></tr></tbody></table>
> 
> Keep runtime ZIP files as small as possible to reduce upload and extraction time.

### Using custom runtimes 

#### Uploading a runtime

1. Navigate to **Settings > Custom runtimes** in your tenant if you are a tenant or team admin(users with `STORY_MANAGE` permission).
2. Click **Add runtime** and upload your ZIP file or choose a runtime from our list of pre-built options.
3. Select a **primary team** that will own the runtime.
4. Optionally grant access to other teams via the **Access** tab.
5. Click **Add** to submit.

While uploading, the status will show as **Creating...**. When extraction completes, the status changes to **Available**.

![](https://www.datocms-assets.com/55802/1769626903-screenshot-2026-01-28-at-19-01-20.png)

### Using a runtime in Run Script

Once a runtime is available, it appears in the **Runtime** dropdown on Run Script actions for **teams with access**. Select your custom runtime and write your script as normal.


![](https://www.datocms-assets.com/55802/1769627007-screenshot-2026-01-28-at-19-03-11.png)

### File paths in scripts

Custom runtimes are extracted to Amazon EFS ([Elastic File System](https://aws.amazon.com/efs/)), a network file system that persists your runtime files. The bootstrap script sets the working directory to your extracted runtime before executing your script. This means you can include additional files in your ZIP (configuration files, certificates, data files) and reference them with relative paths. Example:

- ✅ 'config_file': 'my-config-file.py'
- ❌ 'config_file': '/my-config-file.py'

## Building your custom runtime

### Runtime environment

Custom runtimes run on **provided.al2023**, which uses [Amazon Linux 2023](https://docs.aws.amazon.com/linux/al2023/ug/lambda.html) as the base operating system. Lambda functions use x86_64 architecture. 
**Important:** Build on Amazon Linux 2023 (or compatible) to match Lambda's runtime. Building on Debian/Ubuntu may cause GLIBC version mismatches.


### Runtime structure

Your ZIP should contain:

```
my-runtime.zip
├── bin/
│   └── python3 (or python3.12, python3.13, etc.)
├── lib/
│   └── python3.12/
│       └── site-packages/ (your packages)
└── handler (optional - see Custom handlers)
```

### Building with Docker

Using the following Dockerfile as an example:

```bash
# Use Amazon Linux 2023 base image to match Lambda runtime
FROM --platform=linux/amd64 amazonlinux:2023 AS builder

# Install build dependencies
RUN dnf update -y && \
    dnf groupinstall -y "Development Tools" && \
    dnf install -y wget openssl-devel zip && \
    dnf clean all

# Download and compile Python 3.12
WORKDIR /tmp
RUN wget https://www.python.org/ftp/python/3.12.7/Python-3.12.7.tar.xz && \
    tar -xf Python-3.12.7.tar.xz && \
    cd Python-3.12.7 && \
    ./configure --prefix=/opt/python-runtime && \
    make -j$(nproc) && \
    make install

# Create runtime structure
RUN mkdir -p /opt/runtime && \
    cp -a /opt/python-runtime/* /opt/runtime/

# Install packages
RUN /opt/runtime/bin/python3 -m pip install --upgrade pip && \
    /opt/runtime/bin/python3 -m pip install netmiko==4.2.0

# Create ZIP
WORKDIR /opt/runtime
RUN zip -ry /tmp/python-netmiko-runtime.zip .
```

Build and extract the ZIP:

```bash
# Build the image
docker build --platform linux/amd64 -f Dockerfile -t python-netmiko-runtime .

# Extract the ZIP
docker rm -f temp 2>/dev/null || true
docker create --name temp python-netmiko-runtime
docker cp temp:/tmp/python-netmiko-runtime.zip ./python-netmiko-runtime.zip
docker rm temp
```

### Validating your runtime

Validate your runtime ZIP locally before uploading to catch common issues early:

```
docker run --rm \
  --platform linux/amd64 \
  -v $(pwd):/runtime \
  -w /runtime \
  amazonlinux:2023 \
  /bin/bash -c "dnf install -y unzip > /dev/null 2>&1 && \
    unzip -q python-netmiko-runtime.zip -d /tmp/test && \
    export LD_LIBRARY_PATH=/tmp/test/lib:\$LD_LIBRARY_PATH && \
    /tmp/test/bin/python3 -c 'import sys; print(f\"Python {sys.version}\")' && \
    /tmp/test/bin/python3 -c 'import encodings; print(\"✓ encodings OK\")' && \
    /tmp/test/bin/python3 -c 'import netmiko; print(f\"✓ netmiko {netmiko.__version__} OK\")' && \
    echo '✅ All validation tests passed!'"
```

This runs in a Lambda-like environment, so passing these checks indicates your runtime should work in Lambda.

### Building with the custom-runtime-builder tool

**Note:** This is an unofficial tool and is not actively maintained by Tines. Use at your own discretion.

A helper tool for creating, building, and testing custom runtimes without writing Dockerfiles. Requires Docker with linux/amd64 support and bash.

[custom-runtime-builder.zip](https://www.datocms-assets.com/55802/1770715795-custom-runtime-builder.zip)

```bash
# Create a new environment
./build-env.sh new my-project

# Edit environments/my-project/config.env to set Python version
# Edit environments/my-project/requirements.txt to add dependencies

# Build and test
./build-env.sh build my-project
./build-env.sh test my-project
```

The output ZIP at `builds/my-project-runtime.zip` can be uploaded via **Settings > Custom runtimes**. Run `./build-env.sh` list to see available environments, or `./build-env.sh info <env>` for details.

### How custom runtimes execute in Cloud

### Handler

The handler executes your user script. It receives the event `{"user_script": "...", "input": {...}}`, extracts and executes the `main(input)` function, and returns `{"output": <result>}`.By default, Tines generates a handler that includes:

- AST validation (ensures scripts define main())
- Restricted built-ins for security
- Error handling with structured responses
- stdout/stderr capture and logging

### Bootstrap

The bootstrap script is the Lambda entry point. It:

1. Mounts your runtime from EFS
2. Finds the Python executable in your runtime's bin/ directory
3. Validates the runtime
4. Executes the handler
5. Communicates with the Lambda Runtime API

## Custom handlers

You can provide your own handler script instead of using the default.

### Use cases

- Different script structure (class-based handlers, multiple entry points)
- Custom logging, metrics, or telemetry
- Framework integration (async execution, middleware)
- Multi-file script support
- Custom security restrictions
- One-time setup/initialization

### Providing a custom handler

Place a file named handler (or bin/handler) in your runtime ZIP. The bootstrap script checks for handlers in this order:

1. handler (root of your runtime)
2. bin/handler (fallback location)
3. Generated handler (if none found)

### Handler requirements

Your custom handler must:

- Be executable (`chmod +x handler`)
- Read JSON from stdin: `{"user_script": "...", "input": {...}}`
- Write JSON to stdout:` {"output": <result>}`
- Handle errors by returning structured error responses

Example custom handler:

```python
#!/usr/bin/env python3
import json
import sys
import os

# One-time setup (runs on first invocation per container)
SETUP_FLAG = "/tmp/.handler_setup_done"
if not os.path.exists(SETUP_FLAG):
    os.environ["MY_API_ENDPOINT"] = "https://api.example.com"
    open(SETUP_FLAG, "w").close()

# Read event from stdin
event = json.load(sys.stdin)
user_script = event.get("user_script", "")
input_data = event.get("input", {})

# Execute user script
exec(user_script)
result = main(input_data)

# Return JSON output
print(json.dumps({"output": result}))
sys.stdout.flush()
```

## VPC access and networking

Custom runtime Lambda functions run in a VPC (required for EFS). To connect to databases, APIs, or on-premises resources, you can configure network access using egress IP addresses.

### Getting egress IP addresses

1. Call the `/info` API endpoint: GET `https://your-tenant.tines.com/info`
2. Find `lambda_egress_ips` in the response
3. Allowlist these IPs in your firewall or security rules
4. Set **Networking: Dedicated** on your Run Script action to use these IPs

```bash
curl https://your-tenant.tines.com/info
# Response includes: "lambda_egress_ips": ["52.50.126.105", "99.81.187.154"]
```

**Note:** Egress IPs are static and typically include 2 IPs (one per Availability Zone). On multi-tenant stacks, these IPs are shared with other customers unless you are using a dedicated tenant.

## Best practices

1. **Validate locally before uploading.** Use the validation command to catch issues before they cause failed action runs.
2. **Keep runtimes minimal.** Only include packages you need. Smaller runtimes upload and extract faster.
3. **Pin package versions.** Specify exact versions (e.g., netmiko==4.2.0) for reproducible builds.
4. **Build on Amazon Linux 2023.** This ensures GLIBC compatibility with the Lambda runtime.
5. **Test with the same architecture.** Build and test with --platform linux/amd64 to match Lambda's x86_64 architecture.

#### Case

### Running code in Tines

While you can build nearly anything using Tines actions, you may find yourself wanting to solve a specific problem using code. Tines supports multiple ways of doing this, depending on your specific requirements end environment.

This is an overview of the different options:

| Feature | AWS Lambda | tines-command-runner | command-over-http |
| --- | --- | --- | --- |
| Documentation | [Cloud Run Script Action Documentation](https://www.tines.com/docs/actions/tools/run-script/) | [tines-command-runner documentation](https://www.tines.com/docs/self-hosting/additional-applications/run-script-for-self-hosted/)  
  
  
[Run-Script-over-Tunnel documentation](https://www.tines.com/docs/admin/tunnel/run-script-over-tunnel/) | [command-over-http documentation](https://www.tines.com/docs/admin/command-over-http/) |
| Primary use case(s) | \- Zero-config Python running  
  
 | \- Self-hosted tenants  
\- Cloud customers who need to network with private resources  
\- Avoiding limitations of Cloud Run Script | \- Legacy use  
\- Using non-standard binaries  
\- Running Bash or PowerShell |
| Installation requirements | None | Customer needs to run the tines-command-runner container in their network, see requirements. | Customer needs to run the command-over-http container in their network. See requirements. |
| Cloud support | Yes, natively used via Run Script Action | Yes, via Run-Script-over-Tunnel | Yes, via Tunnel on an HTTP Request Action |
| Self-hosted Support | No, only via [story library](https://www.tines.com/library/stories/1182841/?name=create-update-and-run-aws-lambda-functions&redirected-from=%2Flibrary%2Fstories%2F). | Yes, as a sidecar container. | Yes, as a sidecar container. |
| Runtime Support | Python (version 3.13 and [custom runtimes](https://www.tines.com/docs/actions/tools/run-script/custom-runtimes/)), AWS CLI | Python (version configurable), Bash | Python 3, Bash, PowerShell 7 |
| Dependencies | Public PyPi packages | Public or Private PyPi Packages via PIP\_INDEX\_URL | Fixed. Customers can extend and run their own Docker image |
| Max timeout | 110 seconds | Default 60 seconds. Configurable via RUN\_SCRIPT\_MAX\_TIMEOUT | Default 300 seconds. Configurable via EXECUTION\_TIMEOUT |
| Maximum input payload | 6MB | No limit, depends on infrastructure | No limit, depends on infrastructure |
| Maximum output payload | 20MB | Maximum event size, depends on infrastructure | Maximum event size, depends on infrastructure |
| Networking | Public internet connectivity, floating IP address | Customer’s networking | Customer’s networking |
| Underlying resources | 1 GB Memory, 0.5 vCPU, 756GB Ephemeral storage. | Customer’s settings, bound by container configuration | Customer’s settings, bound by container configuration |

### Working with files

## Overview

Files are typically handled in Tines through base64 encoded strings. Base64 encoding allows Tines include binary data in events and, as such, any action type may interact with a file. The [Receive Email Action (IMAP mode)](https://hub.tines.com/docs/actions/types/imap) and [HTTP Request Action](https://hub.tines.com/docs/actions/types/http-request) provide specific features for working with files.

## IMAP Action

Every event emitted by an IMAP action includes an array containing information on all files attached to the corresponding email. For example, in the below event, we see a single file, hello.txt, was attached to the email. We also have various pieces of metadata about the file including the file name and hashes. The field `base64encodedcontents` contains a Base64 representation of the file.

```json
{
  "message_id": "1688375064.8603887.1514928714437@example.com",
  "folder": "INBOX",
  "subject": "This is the subject of the email",
  "from": "bob@example.com",
  "to": ["alice@example.com"],
  "cc": ["carol@example.com"],
  "date": "2018-01-01T10:10:00+00:00",
  "mime_type": "text/plain",
  "body": "This is the body of the email.",
  "has_attachment": true,
  "attachments": [
    {
      "filename": "hello.txt",
      "guid": "dee73fe0-044f-4e2d-873e-e6850debc03a",
      "md5": "aba2d86ed17f587eb6d57e6c75f64f05",
      "sha256": "807126cbae47c03c99590d081b82d5761e0b9c57a92736fc8516cf41bc564a7d",
      "sizeinbytes": 1578,
      "base64encodedcontents": "ug4AtAnNIbgBTM0hVGhpc=="
    }
  ]
}
```

## HTTP Request Action

It is common for web applications to accept file uploads. These uploads are typically processed by sending the web application a [multipart/form-data](https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2) request. We can use the HTTP Request action to send files to 3rd-party systems using:

- a `multipart/form-data` request
- a field containing the Base64 encoded representation of a file
- the [`BASE64_DECODE`](https://www.tines.com/docs/formulas/functions/base64-decode) function

For example, the following HTTP Request action will submit a file from an incoming event to Virustotal:

```json
{
  "url": "https://www.virustotal.com/vtapi/v2/file/scan",
  "content_type": "data",
  "method": "post",
  "payload": {
    "file": {
      "contents": "=BASE64_DECODE(get_email_with_attachment.attachments[0].base64encodedcontents)",
      "filename": "=get_email_with_attachment.attachments[0].filename"
    },
    "apikey": "=CREDENTIAL.virustotal"
  },
  "headers": {}
}
```

As shown above, we use the `data` content_type to indicate we wish to submit the request using `multipart/form-data`. We then build a 'file' object, taking the encoded content contained in the `.get_email_with_attachment.attachments[0].base64encodedcontents` field from the incoming event and decoding it back to its original form using the [`BASE64_DECODE`](https://www.tines.com/docs/formulas/functions/base64-decode) function, we include this in a field called 'contents'. We must also include a 'filename' field in the object. In this example, the object is labeled `file`, but you can use whatever label you like – Tines will recognize the object based on its `contents` and `filename` structure.

Tines will automatically detect the content type based on file contents, but if you need to control this directly, you can specify `content_type` alongside the `contents` and `filename` keys.

When the above action runs, it will build and send a correctly formatted multipart request to the specified URL.

Other APIs may require that file information is uploaded in base 64, for instance, for this other Virustotal API endpoint:


```json
{
  "url": "https://www.virustotal.com/api/v3/files",
  "method": "post",
  "content_type": "application_json",
  "payload": {
    "file": "=BASE64_ENCODE(upstream_action.file_contents)"
  },
  "headers": {
    "x-apikey": "YOUR_API_KEY"
  }
}
```

### Time saved

## Overview

You can record metrics for any action in the `Time saved` block of the Action Status tab. These metrics can then be viewed on the Reporting page in the Reporting overview chart.

## How to set up time saved

### 1) Publish story to a team

Metrics are only recorded for stories which are **published** in teams. You can publish a story through the button at the top-right of the Story page. This is relevant for stories within draft teams.

**

### 2) Set time saved metrics

Select the action you want to record metrics for. Then open the Status tab in the right hand panel and click the toggle within the `Time saved` section. This will open a popover with a series of metrics with modifiable units and values.

**

### 3) View data

And that's it! As your actions with time saved metrics run, the accumulated data will be displayed on the Reporting overview chart on the reporting page. As more metrics are added, more categories will be added to this graph.

**

### Shortcuts

There are a number of shortcut utilities built into Tines to reduce the time spent configuring new Actions from existing resources. 

## Copy and Paste

Each Action on the Storyboard is a representative JSON object that can be added to your computer's clipboard just like any word processing application. When copied to the clipboard and pasted into a text editor, the Action configuration is represented as the example below:

```json
{
  "agents": [
    {
      "disabled": false,
      "name": "Event Transform Action",
      "options": "{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"message\":\"This is an automatically generated message from Tines\"}}",
      "position": {
        "x": -1155,
        "y": -30
      },
      "type": "eventTransformation"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

This allows for Actions to be easily duplicated in a Story, transferred between Stories, or moved across different instances of Tines. When multiple Actions and their associated links are selected, they are all copied to the clipboard and can be pasted into a new Story.

**

## Import from cURL

Many services provide example [cURL](https://curl.se/) commands in order to interact with their API. Tines can utilize these cURL commands to create new Actions on the Storyboard. Copy to your computer's clipboard the cURL command of the API resource you wish to create an Action for. With the Storyboard selected, simply paste the cURL command to create a new Action which will be configured with the options provided from the cURL command.

![](https://www.datocms-assets.com/55802/1655990689-shortcuts_curl_to_tines-be91d36c663a5a17d42f75f362a17f20.gif)

## Keyboard shortcuts menu

> **NOTE:** If you hit ⇧ (Shift) + K anywhere in Tines, it will bring up the keyboard shortcuts menu.

![](https://www.datocms-assets.com/55802/1756904266-screenshot-2025-09-03-at-10-41-11.png)

The keyboard shortcuts menu provides quick access to commands based on your current context. It displays different options depending on where you are in the app.

Press `⇧ K` (Shift+K) anywhere in Tines to see available shortcuts for your current context.

### In app shortcuts

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Keyboard shortcut</td><td>Description</td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmxpc3QlMjIlMkMlMjJzdHlsZSUyMiUzQSUyMmJ1bGxldGVkJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjIlRTIlOEMlOTglMjBLJTIyJTJDJTIyY29kZSUyMiUzQXRydWUlN0QlNUQlN0QlNUQlN0QlNUQlN0QlNUQ="><p><code>⌘ K</code></p></div></td><td>Opens the command bar</td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmxpc3QlMjIlMkMlMjJzdHlsZSUyMiUzQSUyMmJ1bGxldGVkJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjIlRTIlOEMlOTglMjAlRTIlODclQTclMjBNJTIyJTJDJTIyY29kZSUyMiUzQXRydWUlN0QlNUQlN0QlNUQlN0QlNUQlN0QlNUQ="><p><code>⌘ ⇧ M</code></p></div></td><td>Navigates to Workbench</td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmxpc3QlMjIlMkMlMjJzdHlsZSUyMiUzQSUyMmJ1bGxldGVkJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjIlRTIlOEMlOTglMjAlRTIlODclQTclMjBFJTIyJTJDJTIyY29kZSUyMiUzQXRydWUlN0QlNUQlN0QlNUQlN0QlNUQlN0QlNUQ="><p><code>⌘ ⇧ E</code></p></div></td><td>Search and switch teams</td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmxpc3QlMjIlMkMlMjJzdHlsZSUyMiUzQSUyMmJ1bGxldGVkJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjIlRTIlOEMlOTglMjAlRTIlODclQTclMjBLJTIyJTJDJTIyY29kZSUyMiUzQXRydWUlN0QlNUQlN0QlNUQlN0QlNUQlN0QlNUQ="><p><code>⌘ ⇧ K</code></p></div></td><td>Toggle light/mode</td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmxpc3QlMjIlMkMlMjJzdHlsZSUyMiUzQSUyMmJ1bGxldGVkJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjIlRTIlOEMlOTglMjAlRTIlODclQTclMjAxJTIyJTJDJTIyY29kZSUyMiUzQXRydWUlN0QlNUQlN0QlNUQlN0QlNUQlN0QlNUQ="><p><code>⌘ ⇧ 1</code></p></div></td><td>Toggle feature flags</td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmxpc3QlMjIlMkMlMjJzdHlsZSUyMiUzQSUyMmJ1bGxldGVkJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnR5cGUlMjIlM0ElMjJwYXJhZ3JhcGglMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjIlRTIlOEUlOEIlMjIlMkMlMjJjb2RlJTIyJTNBdHJ1ZSU3RCU1RCU3RCU1RCU3RCU1RCU3RCU1RA=="><p><code>⎋</code></p></div></td><td>Dismisses any open modals</td></tr></tbody></table>

### Storyboard shortcuts

<!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}-->

<!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}-->

<table xmlns="http://www.w3.org/1999/xhtml" cellspacing="0" cellpadding="0" dir="ltr" border="1" data-sheets-root="1" data-sheets-baot="1"><colgroup><col width="100"><col width="100"></colgroup><tbody><tr><td>Keyboard shortcuts</td><td>Description</td></tr><tr><td><code>1</code></td><td>Zoom to fit</td></tr><tr><td><code>-</code></td><td>Zoom out</td></tr><tr><td><code>⌘ +</code></td><td>Zoom in</td></tr><tr><td><code>0</code></td><td>Zoom to 100%</td></tr><tr><td><code>T</code></td><td><div><div>Toggle templates</div></div></td></tr><tr><td><code>⌘ E</code></td><td>Toggle events</td></tr><tr><td><code>⌘ A</code></td><td>Select all</td></tr><tr><td><code>⌘ G</code></td><td>Group actions</td></tr><tr><td><code>⌘ V</code></td><td>Paste actions</td></tr><tr><td><code>⌫</code></td><td>Delete items</td></tr><tr><td><code>⌘ ⇧ <google-sheets-html-origin>⌥</google-sheets-html-origin> ⏎</code></td><td><div><div>Re-emit last event</div></div></td></tr><tr><td><code>R</code></td><td>Rename</td></tr><tr><td><code>N</code></td><td>Add note</td></tr><tr><td><code>/</code></td><td>Search</td></tr><tr><td><code>W</code></td><td>Scroll to start</td></tr><tr><td><code>Q</code></td><td><div><div>Center on selection</div></div></td></tr><tr><td><code>⌘ Z</code></td><td>Undo</td></tr><tr><td><code>⌘ ⇧ Z</code></td><td>Redo</td></tr><tr><td><code>⌘ /</code></td><td><div><div>Toggle all panels</div></div></td></tr><tr><td><code>⌘ ⇧ P</code></td><td><div><div>Pin/unpin action to events</div></div></td></tr><tr><td><code>⎋</code></td><td>Unselect all</td></tr><tr><td><code>⌘ ⇧ ⏎</code></td><td>Run action</td></tr><tr><td><code>⌘ ⇧ D</code></td><td>Test action</td></tr><tr><td><code>⌘ J</code></td><td><div><div>Toggle tool action</div></div></td></tr><tr><td><code>⇧ D</code></td><td><div><div>Toggle storyboard logging</div></div></td></tr></tbody></table>

### Pages shortcuts

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Keyboard shortcuts</td><td>Descriptions</td></tr><tr><td><code>⌘ <span>↵</span></code></td><td>Submit page (when there is only one button on the page)</td></tr></tbody></table>

### Notable Cases shortcuts

See cases shortcuts [here](https://www.tines.com/docs/cases/overview/shortcuts/).

### Notable Workbench shortcuts

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Keyboard shortcuts</td><td>Descriptions</td></tr><tr><td><code>N</code></td><td>Starts a new chat</td></tr><tr><td><code>/</code></td><td>Focuses on the chat input</td></tr><tr><td><code>⌘ [</code></td><td>Toggles the left sidebar</td></tr></tbody></table>

These shortcuts help you work and navigate efficiently in Tines.

## Credentials

## Introduction

It is a common requirement to need to include sensitive information such as API tokens and passwords in actions. As other users can see action configurations, Tines provides the [CREDENTIAL formula key](https://www.tines.com/docs/formulas/referencing-data#from-credentials) which allows users to reference this sensitive information without exposing the value in Tines. When an action containing this [CREDENTIAL formula key](https://www.tines.com/docs/formulas/referencing-data#from-credentials) runs, Tines will substitute in the corresponding credential value. This means that sensitive data does not need to be stored as plaintext in Action configuration options or in a resource.

## Creating a Credential

Navigate to the credentials page by selecting your team name in the menu on the upper left side and selecting "Credentials". From the credentials page, select "New". You will then be prompted to choose the credential type.

Tines supports the following types of credentials:

- [Text](https://hub.tines.com/docs/credentials/text)
- [JWT tokens](https://hub.tines.com/docs/credentials/jwt)
- [OAuth2.0](https://hub.tines.com/docs/credentials/oauth)
- [AWS](https://hub.tines.com/docs/credentials/aws)
- [HTTP Request](https://hub.tines.com/docs/credentials/http-request)
- [Mutual TLS](https://hub.tines.com/docs/credentials/mtls)
- [Multi Request](https://tines.com/docs/credentials/multi-request)

and additionally has product-specific [connect flows](https://www.tines.com/docs/credentials/connect-flows/) that will guide you through the credential creation process per product.

> **TIP:** See our [Authentication Guides](https://explained.tines.com/en/collections/3801629-authentication-guides) for step-by-step instructions to create credentials for commonly used products in Tines.

## Security Considerations when using credentials

While this method dramatically increases the security of secret information in Tines, like any piece of security, it’s not absolute. For example, suppose Tines is interacting with a 3rd-party service. In that case, the service may insecurely include the credential’s value in its response, which Tines will then include in an emitted event. However, Tines does build in flexible options for you to reduce visibility of sensitive data, such as [customizing an action's event output](https://www.tines.com/whats-new/customize-an-actions-event-output) to modify the event data the action would produce and remove sensitive data for example.

### Text

Text credentials are simple plain-text strings. When an action containing a [`CREDENTIAL` formula expression](https://www.tines.com/docs/formulas/referencing-data#from-credentials) corresponding to a text-type credential runs, Tines will substitute in the secret text.

### AWS

## Introduction

Using Tines to automate interaction with AWS services requires the use of an AWS credential. When a HTTP Request Action with an AWS mode credential runs, Tines will authorize the request AWS using the [Signature Version 4 Signing Process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) and include the corresponding headers in the request.

## Creating an AWS credential

### Role-based access

AWS [recommends using roles](https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html#use-roles) for cross account access - e.g. to allow Tines to access resources in your AWS account. To create a Role-based access AWS credential in Tines, you'll also need to create and correctly configure a Role in your AWS account. The following three steps will get you up and running. For more information, see the [AWS tutorial](https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html).

Role-based access is not available on self-hosted environments.

#### Step 1: Create your AWS Credential in Tines

Create a new Credential in Tines. Select "Manual creation" then select the "AWS" option. 

![](https://www.datocms-assets.com/55802/1762872251-screenshot-2025-11-11-at-14-40-10.png)

Set the "Authentication type" to "Role-based access" and enter a name (and, optionally, a description). 

![](https://www.datocms-assets.com/55802/1762872328-screenshot-2025-11-11-at-14-42-35.png)

You can also choose whether to use a unique generated external ID or to use a team-scoped static external ID. If a team-scoped static external ID does not already exist then one will be generated.

Once you click "Save credential", you'll be presented with an Account ID and External ID:

![](https://www.datocms-assets.com/55802/1762872388-screenshot-2025-11-11-at-14-46-11.png)

You'll need these values in the next step.

#### Step 2: Create your Role in AWS

From the Identity and Access Management (IAM) section of the AWS console, navigate to "Roles" and click on "Create role":

![](https://www.datocms-assets.com/55802/1656001671-create_a_role_in_aws-1f51c0c0d3c7ee8090ed26b2575cb973.png)

For "Trusted entity type", select "Custom Trust Policy" and copy-paste the following trust policy, giving the two Tines AWS accounts permission to assume this role, using the External ID from step 1.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "857223745291"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "ExternalId"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "825838939522"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "ExternalId"
        }
      }
    }
  ]
}

```

![](https://www.datocms-assets.com/55802/1733936472-screenshot-2024-12-11-at-16-56-11.png)

Complete the "Add permissions" and "Name, review and create" steps to finish creating your Role.

#### Step 3: Add your Role's ARN to your Tines Credential

In the AWS console, open the Role you just created and copy the ARN:

![](https://www.datocms-assets.com/55802/1656001707-copy_role_arn-50b8a0abfcc4a4c592ba3a8eb13d34c3.png)

Paste it into the Role ARN field of your Credential in Tines and click "Save credential":

![](https://www.datocms-assets.com/55802/1762872536-screenshot-2025-11-11-at-14-48-46.png)

### Static External IDs

By default, a new and unique external ID is generated for each AWS credential created. More information on external IDs can be found in the [AWS docs](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html). Optionally when creating a role-based access AWS credential you can opt to use a static external ID that is scoped to a team. Each credential in the team that uses this option will have the same external ID. 

![](https://www.datocms-assets.com/55802/1762872942-screenshot-2025-11-11-at-14-42-46.png)

If you need to rotate a static external ID you can use the [destroy static external ID API endpoint](https://www.tines.com/api/teams/destroy_static_external_id/) which will delete the current static external ID for that team. A new one can be generated by creating a new AWS credential that uses the static external ID option. Previously created credentials are not affected.

### Key-based access

> **IMPORTANT:** AWS [discourages the use of long-term access keys](https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html#use-roles) in 3rd party tools. Please consider using a role-based access AWS credential instead.

Enter the following information in the AWS New Credential page:

- Credential name: Your desired AWS credential name.
- Access key: The `access key` from your [AWS Security Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).
- Access secret: The `access secret` from your [AWS Security Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).

If you want to [assume a role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html) before performing the action, you can set values for the following fields.

- Assumed Role ARN: The ARN of the role you wish to assume, e.g.: `arn:aws:iam::123456789012:role/write-access-role`

Tines will request a session with the minimum duration (15 minutes).

### Bedrock API key

To authenticate with AWS Bedrock using an API key, select `Bedrock API key` as the authentication type.

Enter the following information in the AWS New Credential page

-  Credential name: Your desired AWS credential name.
- API key: Your Bedrock API key.

This will be used for bearer token authentication.

For more details on Bedrock API keys, see the [AWS documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html)

## Using an AWS credential with a HTTP Request Action

To use an AWS credential with a HTTP Request action, include a [`CREDENTIAL` formula expression](https://www.tines.com/docs/formulas/referencing-data#from-credentials) in the action's `Authorization` header.

## Sample AWS HTTP Request Actions

### Scan a DynamoDB Table

```json
{
  "url": "https://dynamodb.eu-west-1.amazonaws.com",
  "method": "post",
  "content_type": "json",
  "payload": {
    "TableName": "TestTable",
    "AttributesToGet": ["Id"]
  },
  "headers": {
    "Authorization": "<<CREDENTIAL.aws_dynamo_db>>",
    "X-Amz-Target": "DynamoDB_20120810.Scan"
  }
}
```

### List Cloudtrails

```json
{
  "url": "https://cloudtrail.us-east-1.amazonaws.com",
  "method": "get",
  "content_type": "form",
  "payload": {
    "Action": "DescribeTrails",
    "Version": "2013-11-01"
  },
  "headers": {
    "Authorization": "<<CREDENTIAL.aws_cloudtrail>>"
  }
}
```

### List IAM Users

```json
{
  "url": "https://iam.amazonaws.com",
  "content_type": "form",
  "method": "get",
  "payload": {
    "Action": "ListUsers",
    "Version": "2010-05-08"
  },
  "headers": {
    "Authorization": "<<CREDENTIAL.aws_iam>>"
  }
}
```

### HTTP request

## Introduction

The HTTP request credential allows you to make a dynamic request to fetch an access token or API key, every time the credential is used. For instance, you could use this to fetch runtime credentials from a service like [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) or [HashiCorp](https://www.hashicorp.com/en/products/vault).

When used in a HTTP request action, this means that two web requests will be made for each run: one to fetch the credential value, and a second to run the action itself.

## Configuring the credential

### First, set up the HTTP request options

You’ll need to enter the URL, content type, method, and payload for the request you want to make to fetch your credential value. Beyond these basics, you can add any other request option that our HTTP request action supports:

> **TIP:** If you have configured the HTTP request on the storyboard, click **<> Editor** on the action and copy the Options contents. Then, in your credential, click **<> Edit as JSON** and paste in the Options contents. This configures the HTTP request inputs in bulk.

![](https://www.datocms-assets.com/55802/1700664427-cleanshot-2023-11-22-at-09-46-29-2x.png)

Additionally, you can input a secret value for use in the request. This is securely stored, and can be referred to in formulas within the request fields (params, headers, etc) as `secret`. 

![](https://www.datocms-assets.com/55802/1726659411-screenshot-2024-09-18-at-12-36-37.png)

### Next, run a test request

Once your request is configured, press **‘Run request’** to run a test request. The result of the request will be displayed. Tweak and re-run tests until the result is as expected.

### Finally, configure the location of token

Use the formula builder in the **‘Location of token from response’** field to describe the path to the token value in your test request’s response.

Optionally, set a **TTL (time-to-live)** up to a maximum of 2 minutes. This enables actions to store the token that is retrieved, thereby avoiding the need to make multiple requests.

![](https://www.datocms-assets.com/55802/1700664880-cleanshot-2023-11-22-at-09-54-16-2x.png)

That’s it! The token will be obtained from this location of the response every time the credential is used, and its request fires.

> **IMPORTANT:** [Action egress control rules](https://www.tines.com/docs/admin/action-egress-control/) apply to HTTP request type credentials.

### Mutual TLS

## Introduction

[Mutual TLS](https://en.wikipedia.org/wiki/Mutual_authentication) (also known as *MTLS*, *mutual SSL*, *mutual authentication*, or *two-way authentication*) is a requirement of certain endpoints, where the server requires verification of the client’s identity.

Tines’ [HTTP Request Action](https://hub.tines.com/docs/actions/types/http-request) supports mutual TLS through the `mutual_tls` configuration option. This is most easily used alongside a special mutual TLS credential type.

## Creating a mutual TLS credential

Enter the following information in the New Credential page, after selecting mutual TLS from the dropdown:

- **Root certificate**: The root certificate file for the certificate authority (CA) responsible for signatures
- **Client certificate**: The certificate file issued by the CA for this client
- **Client private key**: The private key file for the client certificate

These files will often have the extension `.pem` or `.crt`, but any plain text file will be accepted.

## Using a mutual TLS credential with a HTTP Request Action

Include a [`CREDENTIAL` formula expression](https://www.tines.com/docs/formulas/referencing-data#from-credentials) in the action's `mutual_tls` configuration option.

### Example

For a mutual TLS credential named "mtls_example":

```
{
  "url": "https://internal.tool",
  "method": "get",
  "content_type": "json",
  "mutual_tls": "<<CREDENTIAL.mtls_example>>"
}
```

### JWT

## Introduction

[JSON Web Tokens](https://jwt.io/) (JWT - pronounced “jot”) are used by many services to represent and exchange information in a secure manner. When creating a JWT token, you need to supply the following information:

- **Algorithm**: The algorithm to be used when computing the JWT
- **Payload**: The payload to be included in the JWT. Payloads typically contain a set of [Claims](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims#reserved-claims).
- **Auto generate 'iat' & 'exp' claims**: When using JWTs, two common claims are *iat* (Issued At) and *exp*. When this checkbox is selected, Tines will insert an iat claim corresponding to the time the action runs, and an exp claim corresponding to the current time + 1 hour, into the JWT payload.
- **Private key**: The private key to be used to sign the JWT.

When an action containing a [`CREDENTIAL` formula expression](https://www.tines.com/docs/formulas/referencing-data#from-credentials) referring to a JWT-type credential runs, Tines will build a JWT and insert it into the options block in place of the expression. For example, given  `<<CREDENTIAL.my_jwt_user_credential>>` and a corresponding credential, at runtime, Tines will substitute in a string similar to the following:

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzY290Y2guaW8iLCJleHAiOjEzMDA4MTkzODAsIm5hbWUiOiJDaHJpcyBTZXZpbGxlamEiLCJhZG1pbiI6dHJ1ZX0.03f329983b86f7d9a9f5fef85305880101d5e302afafa20154d094b229f75773
```

### OAuth 2.0

## Introduction

OAuth 2.0 is the industry-standard protocol for authorization.

## Create app

Before you can begin the OAuth process, you must first register a new app with the service you wish to integrate with Tines (e.g.: Google, Facebook, Okta). When registering a new app, you usually register basic information such as application name, website, a logo, etc. In addition, you must register a redirect/callback URL. The callback/redirect URL you should enter when registering the app is provided on the "Create new credential" page and takes the form: `https://<tenant-domain>/oauth2/callback`

## Creating the credential

**Client ID & secret**: After registering your app with the 3rd-party service, you will receive a client ID and a client secret. The client ID is considered public information, the client secret must be kept confidential.

**Scope**: Enter one or more scope values indicating which parts of the user's account you wish to access.

**Grant type**: Tines supports [*client_credentials*](https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/) and [*authorization_code*](https://www.oauth.com/oauth2-servers/access-tokens/authorization-code-request/) grants. Chose the type corresponding to the application you registered.

**OAuth URL and OAuth token URL**: Enter the OAuth URL and OAuth token URL corresponding to the service with which you registered your app. This information is typically provided when registering the app.

## Saving the credential

After entering the required information, press "Save credential". When using an authorization_code grant, you will be directed to the 3rd-party service and asked to authorize access to your account.

## Refreshing the access token

Tines will automatically fetch a new access token for the credential every 30 minutes.

### Credential logs

If refreshing the access token fails, you can navigate to the logs tab to see the logged error message to help with debugging.

### Multi Request

## Introduction

The Multi Request credential allows you to make multiple HTTP requests to fetch an access token or API key, every time the credential is used. For instance, you could use this to fetch runtime credentials from a service like [Vault](https://www.vaultproject.io/) or [CyberArk Secrets Hub](https://www.cyberark.com/resources/cyberark-identity/secrets-hub).  

When used in a HTTP request action, this means that three web requests will be made for each run: two to fetch the credential value, and a third to run the action itself.



![](https://www.datocms-assets.com/55802/1716927701-multi-request.png)

## Configuring the credential

This is our most complex credential type, and there are a few steps involved.

### First, configure HTTP Request 1

You’ll need to enter the URL, content type, method, and payload for the request you want to make to fetch your credential value. Beyond these basics, you can add any other request option that our HTTP request action supports. 

> **TIP:** If you have configured the HTTP request on the storyboard, click **<> Editor** on the action and copy the Options contents. Then, in your credential, click **<> Edit as JSON** and paste in the Options contents. This configures the HTTP request inputs in bulk.

![](https://www.datocms-assets.com/55802/1700664427-cleanshot-2023-11-22-at-09-46-29-2x.png)

Additionally, you can input a secret value for use in the request. This is securely stored, and can be referred to in formulas within the request fields (params, headers, etc) as `secret`.

### Next, run a test request

Once your request is configured, press **‘Run request’** to run a test request. The result of the request will be displayed. Tweak and re-run tests until the result is as expected.

### Then, configure HTTP Request 2

This is the second step needed to get the secret value that will be used in outbound calls to your service. Configure it similarly to how you configured the first request and run a test request.

**Referencing HTTP Request 1**:

The output of HTTP request 1 will be needed in HTTP Request 2. To reference the output of the first request, use the key `PREVIOUS_STEP` and specify the path to the token or specified item needed to successfully make the second request.

### Finally, configure the location of token

Use the formula builder in the **‘Location of token from response’** field to describe the path to the token value in your test request’s response. This will only reference the output of HTTP Request 2.

![](https://www.datocms-assets.com/55802/1700664880-cleanshot-2023-11-22-at-09-54-16-2x.png)

That’s it! The token will be obtained from this location of the response every time the credential is used, and its request fires.

### Credential configuration

## Introduction

The following areas exist within every credential:

**Details tab**:

- Name
- Description
- Additional Configurations:
  
  - [Domain restriction](https://www.tines.com/docs/domain-restriction/)
  - [Credential Metadata](https://www.tines.com/docs/credential-metadata/)
  - [Product](https://www.tines.com/docs/product/)
  - [Access](https://www.tines.com/docs/access/)
- [Expiry](https://www.tines.com/docs/expiry/)



**Other tabs**:

- [Test credential](https://www.tines.com/docs/test-credential-tab/)
- [Actions](https://www.tines.com/docs/actions-tab/)

#### Domain restriction

## Introduction

Domain restriction is used to enhance credential security. In order to use credentials in an outbound request, domain restriction must be defined. 



![](https://www.datocms-assets.com/55802/1716925215-domain-restriction.png)

### Access

Domain restriction can only be changed by the credential creator, users with the ['Manage credentials' permission](https://tines.com/docs/admin/user-administration/custom-roles#feature-permission-list), which corresponds with the [Team admin role](https://tines.com/docs/admin/teams#permissions-table), and tenant owners. 

### Supported restrictions

Domain restriction supports the following:

- Multiple inputs: separate each input with a space
- Wildcards: use a `*` as a wildcard within any of the input types



The following input types are supported:

**Domains**:

Specify the domain of the API call URL that your credential will be used in. 

Examples:

- `*.tines.com` allows your credential to be used in any HTTP request to a URL with a domain that ends in `tines.com`
- `your-domain.tines.com` allows your credential to be used in any HTTP request to a URL with the domain `your-domain.tines.com`

**URL Paths**

URL paths encompass the full URL rather than just the domain. By specifying a URL path, you can prevent usage of a credential within `http` or `https` requests. Additionally, you can further restrict credential usage to a particular part of an API.

Examples:

- `your-domain.tines.com/api/*/cases` allows your credential to be used only within calls to the Tines API for cases. The wildcard allows support for v1 or v2 of the cases API.
- `https://your-domain.tines.com/api/*` allows your credential to be used within calls to the Tines API and further specifies requests must go to an `https` endpoint.

**Server Hosts**

This is used within IMAP mode of the Receive Email action. Enter the server host used for your action to allow your credential to be used within the Receive Email action.

**Special Cases**

We recommend using caution when enabling credentials for the below cases.

- Command over http: to use a credential with command over http, enter `command-over-http`
- Run script: to use a credential in a run script action, enter `run-script`



### Supported functions

Credentials that do not have a domain restriction defined will only be usable in functions that do not expose the credential value. See the allowed functions a credential can be used in below:

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Function</td><td>Parameter index where credential can be used</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/aes-decrypt/">AES_DECRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/aes-encrypt/">AES_ENCRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/tines-encrypt/">TINES_ENCRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/tines-decrypt/">TINES_DECRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/openssl-encrypt/">OPENSSL_ENCRYPT</a></td><td>2 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/openssl-decrypt/">OPENSSL_DECRYPT</a></td><td>2 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/rsa-encrypt/">RSA_ENCRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/rsa-decrypt/">RSA_DECRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/rsa-aes-hybrid-encrypt/">RSA_AES_HYBRID_ENCRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/rsa-aes-hybrid-decrypt/">RSA_AES_HYBRID_DECRYPT</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/jwt-sign/">JWT_SIGN</a></td><td>1 (key)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/hmac-sha1/">HMAC_SHA1</a></td><td>1 (secret)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/hmac-sha1-base64/">HMAC_SHA1_BASE64</a></td><td>1 (secret)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/hmac-sha256/">HMAC_SHA256</a></td><td>1 (secret)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/hmac-sha256-base64/">HMAC_SHA256_BASE64</a></td><td>1 (secret)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/size/">SIZE</a></td><td>0 (text or array)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/zip/">ZIP</a></td><td>2 (password)</td></tr><tr><td><a href="https://www.tines.com/docs/formulas/functions/unzip/">UNZIP</a></td><td>1 (password)</td></tr></tbody></table>

#### Legacy tenants

Tenants created before May 17, 2024 12:00 UTC can opt in to the above behavior via a tenant-wide setting **Credential Access**.

![](https://www.datocms-assets.com/55802/1715794451-credential-access.png)

Before opting in at the tenant level, we strongly recommend enabling this behavior at the credential level to reduce the risk of story breakage. Checking the **Restrict direct access** box will restrict credential usage to only the supported function list and specified domains for the credential.

To view all restricted and unrestricted credentials, tenant owners can visit `https://your-tenant-domain/credentials` and filter for unrestricted credentials. We recommend using this as an additional check before turning on **Restrict direct access** at the tenant level. Learn more about viewing and managing [tenant-wide credentials](https://www.tines.com/docs/tenant-wide-credentials/).

Once all credentials have **Restrict direct access** turned on, we strongly recommend enabling the tenant-wide setting, which will update your tenant to the non-legacy behavior described above, ensuring any new credentials created mandate defined domain restriction to be used in outbound requests.

![](https://www.datocms-assets.com/55802/1715792309-legacy-cred-view.png)

#### Credential Metadata

Oftentimes, credentials will have relevant, non-sensitive information that must be referenced when using them, such as a domain, account number, or username. You can store this data in a key value format as credential metadata.

You can refer to credential metadata using the [INFO formula key](https://www.tines.com/docs/formulas/referencing-data#info), for example `INFO.credential.jira.metadata.domain`.

![](https://www.datocms-assets.com/55802/1704736489-credential-metadata.png)

#### Product

The product field is an optional input to configure for a credential. The field can be populated in 2 ways:

- **Automatically**: once your credential is used with a template, the product field will be populated with the product aligned with that template. This allows easier access when using this product's templates in the future.
- **Manually**: enter or update the aligned product for your credential. If the entered value matches the product name for a template, the credential will be suggested when using that product's templates.

#### Access

### Sharing a Credential

Credentials will, by default, only be accessible to the [Team](https://hub.tines.com/docs/admin/teams) they are created within. Credentials can be configured to be shared with all other teams in the tenant by selecting the 'All teams & drafts' access option or with specific teams. Users can only edit these settings if they have the 'Manage credentials' permission, which corresponds with the Team admin role. Users can only share the credential with teams for which they have the 'Manage credentials' or 'Update credentials' permissions, which correspond to Team admin and Editor roles.

![](https://www.datocms-assets.com/55802/1716925721-access.png)

Need to share with other teams? [Reach out to the team](https://www.tines.com/contact-support) to discuss adding the feature to your Tines tenant.

### Using a shared credential

Stories that call Credentials with the same name as Credentials shared across multiple Teams will use the Credential located within the same Team as the Story.

You can view all credentials available to your team by clicking the "Shared with this team" section in the credentials page. You will not be able to view or modify the contents of the credential unless you have the relevant permissions in the team owning said credential.

![](https://www.datocms-assets.com/55802/1704735825-shared-with-this-team.png)

#### Expiry

Some credentials may require rotation for security compliance, or they may expire at a certain date. Once expired, the credentials may break workflows if they are not updated. Credential expiry settings aid in ensuring that credentials are updated regularly by notifying selected users that the credential is reaching it's expiry date.



![](https://www.datocms-assets.com/55802/1755167777-screenshot-2025-08-14-at-11-35-33.png)



### Expiry date

The expiry date selected should be the UTC date that the credential will expire. Your local time may differ.



### Email reminders

Emails will be sent thirty days, seven days and one day before the credential expires to alert the selected recipients that the credential needs to be updated. Each credential can have up to five recipients, and these can differ between the live and test credential. 

To receive notifications about a credential approaching it's expiry date, a user must be part of the credential's team and have credential update or manage permissions.

#### Test credential tab

## Credential test and production accounts

If you are using [change control](https://www.tines.com/docs/stories/change-control) in a story, you may want to test changes with your developer environment before deploying them to the live story. You can define test credentials in the `Test credential` tab of the Credential. Additionally, you can define related, non-sensitive information about your test environment such as a domain or id in the test metadata section of the `Test credential` tab. In stories with change control enabled, the Test Credential and any test metadata will be used in any `draft` of the story by default. See our [change control](https://www.tines.com/docs/stories/change-control/#use-different-resources-and-credentials-in-a-draft) section for further details.

![](https://www.datocms-assets.com/55802/1776689295-screenshot-2026-04-20-at-13-48-04.png)

#### Actions tab

## See where credentials are used

Select the `Actions` tab to view a list of actions across your tenant using the credential. You can further select any action to view the story in more detail.

![](https://www.datocms-assets.com/55802/1776691944-screenshot-2026-04-20-at-14-31-42.png)

##

### Connect flows

> **TIP:** Don't see a connect flow for your product? Request one [here](https://hq.tines.io/pages/connectflowrequest/).

## Introduction

Connect flows guide users through the process of creating a credential for a specific product. Each connect flow is unique to its product and will create a credential for the user in the team where the connect flow was completed.


Connect flows create various types of credentials (see the pages for your specified product on the left menu), including **managed OAuth** type credentials. Managed OAuth credentials allow users to easily authenticate to a platform by signing in with their account. These managed OAuth connect flows redirect users through `credentials.tines.com` and exchange data via secure browser window-to-window communication. This credential type is not available for self-hosted tenants.

> **IMPORTANT:** No credentials ever leave your tenant as part of any connect flow.

## Create a credential with a connect flow

Connect flows can be accessed in the below areas:

1. **Credentials home**: select `+ New credential` and choose the product you'd like to connect to Tines.
2. **Storyboard: **
  
  1. **Story overview pane**: 
    
    1. Select `+` in the credentials section and choose the product you'd like to connect to Tines.
    2. Select `Connect` by the product name and choose `Create credential` or `New connection`.
    3. Select `Replace` after hovering over the product name and choose `Create credential`.
  2. **Product template**: if you select a template for a product that has a connect flow, a connect button will appear. Select `Connect` and then `New connection` to start the connect flow for that product.



## Connect flow products

For details on each connect flow, view the pages for your specified product on the left menu.

#### Abnormal Security

[Abnormal Security](https://abnormalsecurity.com/) is a cloud-native email security platform that uses artificial intelligence and machine learning to detect and prevent advanced email-based threats, including phishing, business email compromise, and account takeover attacks.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>Access token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Abnormal Security API</td></tr></tbody></table>

> **TIP:** Check out the [Abnormal Security Authentication Guide](https://explained.tines.com/en/articles/8851605-abnormal-security-authentication-guide) for more details on how to authenticate.

#### Abuse.ch

[Abuse.ch](https://abuse.ch/) operates several open threat intelligence projects that track and share data on malware, malicious domains, and botnet infrastructure to help the cybersecurity community detect and block threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>Auth key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the various Abuse.ch products' APIs</td></tr></tbody></table>

#### AbuseIPDB

[AbuseIPDB](https://www.abuseipdb.com/) is a platform that aggregates and provides information about IP addresses reported for abusive behavior, aiding in the detection and mitigation of malicious activities such as hacking, spamming, and other cyber threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the AbuseIPDB API</td></tr></tbody></table>

> **TIP:** Check out the [AbuseIPDB Authentication Guide](https://explained.tines.com/en/articles/7949731-abuseipdb-authentication-guide) for more details on how to authenticate.

#### Acronis

[Acronis](https://www.acronis.com/) provides cybersecurity and data protection solutions, including backup, disaster recovery, and anti-ransomware technologies for businesses and individuals.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Secret</li><li>Data center URL</li></ul><p>Select the 'Copy' button to copy the above and paste as one item into the connect flow to be processed.</p></td></tr><tr><td>Scopes</td><td><div class="modal-body modal-body-content"><span class="ng-star-inserted">The API client will have the same privileges as your account has</span></div></td></tr></tbody></table>

#### Adaptive Shield

[Adaptive Shield](https://www.adaptive-shield.com/) is a SaaS Security Posture Management (SSPM) solution that helps organizations discover, assess, and remediate security risks across their SaaS app ecosystem by providing continuous monitoring, automated policy enforcement, and compliance management.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Base URL</li><li>Account ID</li></ul></td></tr><tr><td>Scopes</td><td><span>The API key generated is limited to the permissions set on the user profile that generated the key.&nbsp;</span></td></tr></tbody></table>

> **TIP:** Check out the [Adaptive Shield Authentication Guide](https://explained.tines.com/en/articles/8572857-adaptive-shield-authentication-guide) for more details on how to authenticate.

#### Airtable

[Airtable](https://airtable.com/) is a cloud-based platform that combines the features of a spreadsheet and a database, allowing users to organize, manage, and collaborate on data in a flexible and customizable way.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>Personal access token</td></tr><tr><td>Scopes</td><td>Defined by the user generating the token.</td></tr></tbody></table>

#### AlienVault OTX

[AlienVault OTX](https://otx.alienvault.com/) (Open Threat Exchange) is a global threat intelligence sharing platform that allows security researchers and IT professionals to collaborate and share information about emerging threats, indicators of compromise, and malicious activities to improve overall cybersecurity defenses.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the AlienVault OTX API</td></tr></tbody></table>

#### Ansible

[Ansible](https://www.ansible.com/) is an open-source automation tool that simplifies configuration management, application deployment, and task automation across multiple servers or systems.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Personal access token</li><li>Ansible host</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user permissions associated with the personal access token</td></tr></tbody></table>

#### Anvilogic

[Anvilogic](https://www.anvilogic.com/) is a cloud-native security operations platform that uses machine learning and automation to help organizations detect, investigate, and respond to cyber threats more efficiently across their entire IT infrastructure.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Anvilogic API</td></tr></tbody></table>

#### ANY.RUN

[ANY.RUN](https://any.run/) is an interactive online malware analysis service that allows users to run and analyze malicious files, URLs, and suspicious activities in a controlled environment.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the ANY.RUN API</td></tr></tbody></table>

> **TIP:** Check out the [ANY.RUN Authentication Guide](https://explained.tines.com/en/articles/7957215-any-run-authentication-guide) for more details on how to authenticate.

#### APIVoid

[APIVoid](https://www.apivoid.com/) offers a range of online tools and APIs for analyzing and monitoring website, domain, and IP address-related data, aiding users in assessing security, reputation, and other relevant information.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the APIVoid API</td></tr></tbody></table>

> **TIP:** Check out the [APIVoid Authentication Guide](https://explained.tines.com/en/articles/7957276-apivoid-authentication-guide) for more details on how to authenticate.

#### Armis

[Armis](https://www.armis.com/) is a cybersecurity platform that provides asset discovery, risk management, and threat detection for all connected devices in an organization's network, including IoT, OT, and unmanaged assets.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Secret key</li><li>Base URL</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Armis Security API</td></tr></tbody></table>

> **TIP:** Check out the [Armis Authentication Guide](https://explained.tines.com/en/articles/8504707-armis-authentication-guide) for more details on how to authenticate.

#### Asana

[Asana](https://asana.com/) is a platform that helps teams organize, track, and manage their work tasks and projects effectively, facilitating collaboration, communication, and productivity within teams.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Asana</td></tr><tr><td>Scopes</td><td><em>default</em>: provides access to all endpoints documented in Asana's <a target="_self" href="https://developers.asana.com/reference/rest-api-reference" style="letter-spacing: 0px;">API reference</a></td></tr></tbody></table>

#### Astrix

[Astrix](https://astrix.security/) secures third-party integrations and non-human identities by detecting, monitoring, and controlling the connections between SaaS applications, APIs, and cloud environments to prevent supply chain attacks.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Astrix API</td></tr></tbody></table>

#### AWS CLI

The [AWS Command Line Interface](https://aws.amazon.com/cli/) (AWS CLI) is a unified tool to manage your AWS services. 

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>AWS</td></tr><tr><td>Required inputs</td><td><ul><li>Role ARN</li><li>Credential name</li><li>ID of team to create credential in</li></ul><p>Note: you will need to update the trust policy of the IAM role to reflect the created external ID of your AWS credential. Ensure you have access to edit your IAM role in AWS before starting the connect flow.</p></td></tr><tr><td>Scopes</td><td>Allows access to run any AWS CLI command that your selected IAM role has permissions to run.</td></tr></tbody></table>

> **TIP:** Check out the [AWS CLI docs](https://www.tines.com/docs/actions/templates/run-python-script/#aws-cli-support) for more details on how to the AWS CLI works in Tines.

#### Axonius

[Axonius](https://www.axonius.com/) provides a cybersecurity asset management platform that helps organizations gain comprehensive visibility and control over their IT assets, ensuring security and compliance across all devices, users, and software.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>API secret</li><li>Hostname</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Axonius API</td></tr></tbody></table>

#### BambooHR

[BambooHR](https://www.bamboohr.com/) is a human resources software solution that helps organizations manage employee data, streamline HR processes, and improve overall workforce management efficiency.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>BambooHR API key</li><li>BambooHR domain</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the API key.</td></tr></tbody></table>

> **TIP:** Check out the [BambooHR Authentication Guide](https://explained.tines.com/en/articles/7982695-bamboohr-authentication-guide) for more details on how to authenticate.

#### Bayse.io

[Bayse.io](https://www.bayse.io/) provides a platform for automated security data analysis, helping organizations detect, investigate, and respond to threats using AI-driven analytics.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Bayse.io API</td></tr></tbody></table>

#### Black Kite

[Black Kite](https://blackkite.com/) is a third-party cyber risk intelligence platform that delivers transparent, standards-based technical, financial, and compliance risk ratings to continuously assess vendors’ cybersecurity posture.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:<br><ul><li>Application (client) ID</li><li>Client secret value</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Black Kite API</td></tr></tbody></table>

#### BloodHound

[BloodHound](https://specterops.io/bloodhound-enterprise/) is a fully managed SaaS platform developed by SpecterOps that enables organizations to continuously identify, prioritize, and eliminate identity-based attack paths within Active Directory, Entra ID, and hybrid environments, thereby reducing the risk of lateral movement and privilege escalation.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>At various steps you will be prompted to enter the following:<br><ul><li>API Key</li><li>ID</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the permissions of the associated user, personal or non-personal</td></tr></tbody></table>

#### Box

[Box](https://www.box.com/home) is a cloud content management and file sharing platform that enables businesses to securely store, access, and collaborate on documents, presentations, and other files from anywhere, on any device.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Box</td></tr><tr><td>Scopes</td><td><ul><li>Read all files and folders stored in Box.</li><li>Write all files and folders stored in Box.</li></ul><p>Access to content is further restricted by the authenticating users' permissions.</p></td></tr></tbody></table>

#### Bugcrowd

[Bugcrowd](https://www.bugcrowd.com/) is a crowdsourced cybersecurity platform that connects organizations with a global network of ethical hackers to identify and report security vulnerabilities in their systems, applications, and networks.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Bugcrowd API</td></tr></tbody></table>

#### Checkmarx

[Checkmarx](https://checkmarx.com/) helps organizations secure their software by providing tools that scan source code, open-source libraries, and application behavior to identify and fix security vulnerabilities early in the development process.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Tenant name</li><li>Tenant region</li></ul></td></tr><tr><td>Scopes</td><td><p>Determined by the user creating the API key.</p><p>Note: the minimum required roles for running an end-to-end flow of scanning a project and viewing results are the out-of-the-box composite role 'ast-scanner' as well as the IAM role 'default-roles'</p></td></tr></tbody></table>

#### Cisco Umbrella

[Cisco Umbrella](https://umbrella.cisco.com/) is a cloud-based security platform that provides advanced threat protection for users, devices, and networks, by blocking malicious internet destinations before a connection is established, thereby enhancing security and minimizing cybersecurity risks.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>API key secret</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the API key.</td></tr></tbody></table>



> **TIP:** Check out the [Cisco Umbrella Authentication Guide](https://explained.tines.com/en/articles/7982855-cisco-umbrella-authentication-guide) for more details on how to authenticate.

#### Claroty xDome

[Claroty xDome](https://claroty.com/industrial-cybersecurity/xdome) is a comprehensive cybersecurity platform that provides visibility, threat detection, and risk management across IT, OT, IoT, and IIoT environments to protect industrial and enterprise networks from cyber threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Defined by the user generating the API token.</td></tr></tbody></table>

#### Claude

[Claude](https://claude.com/product/overview) is a family of advanced generative AI assistants and large language models developed by Anthropic that are designed to engage in safe, helpful and ethical conversational and problem-solving tasks.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li></ul></td></tr><tr><td>Scopes</td><td>Access depends on the permissions available to the associated users Claude profile.</td></tr></tbody></table>

#### ClickUp

[ClickUp](https://clickup.com/) is a cloud-based, all-in-one productivity platform that unifies task management, document collaboration, communication, whiteboarding, automation, AI tools, and more to help teams streamline workflows and boost efficiency.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:<br><ul><li>Application (client) ID</li><li>Client secret value</li></ul></td></tr><tr><td>Scopes</td><td><p>Allows access to all endpoints in the ClickUp API</p></td></tr></tbody></table>

#### Cloudflare

[Cloudflare](https://www.cloudflare.com/) is a global network service provider that offers content delivery network (CDN) services, DDoS protection, Internet security, and distributed domain name server services to improve the security, performance, and reliability of websites and Internet applications.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>Account ID</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the API token</td></tr></tbody></table>

#### Clutch Security

[Clutch](https://www.clutch.security/) is a Universal Non-Human Identity Security Platform that offers complete security and control over NHIs across the entire enterprise.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Access key</li><li>Console domain</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the access key.</td></tr></tbody></table>

#### Code42

[Code42](https://www.code42.com/) is a data security company that provides insider risk management, data loss prevention, and cyber resilience solutions to protect sensitive corporate information from insider threats and data breaches.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client Secret</li><li>Base URL</li></ul></td></tr><tr><td>Scopes</td><td>Scopes are determined by the user creating the API client</td></tr></tbody></table>

#### Confluence

[Confluence](https://www.atlassian.com/software/confluence) is a collaborative workspace software that allows teams to create, organize, and share knowledge, documents, and project information in a centralized digital environment.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Account email associated with the API key</li><li>Confluence domain</li></ul></td></tr><tr><td>Scopes</td><td>The API key generated is limited to the permissions set on the user profile that generated the key. Customers often create an account for Tines to associate the API key with, scoping the permissions for that user as desired.</td></tr></tbody></table>

#### CoreLogic

[CoreLogic](https://www.corelogic.com/) is a leading provider of property information, analytics, and data-enabled solutions that helps real estate, mortgage finance, insurance, and other professionals assess, manage, and mitigate risk through property data and insights.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP request</td></tr><tr><td>Required inputs</td><td><ul><li>Client key</li><li>Secret</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the CoreLogic API</td></tr></tbody></table>

#### Cortex XDR

[Cortex XDR](https://www.paloaltonetworks.com/cortex/cortex-xdr) is a unified, AI-driven security platform that correlates endpoint, network, cloud and identity data to detect, investigate and respond to advanced threats from a single agent and console.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>API Key ID</li><li>Region</li><li>Company Name</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Cortex XDR API</td></tr></tbody></table>

#### Cribl

[Cribl](https://cribl.io/) is a data observability company that offers innovative solutions for managing, processing, and routing large volumes of machine data in real-time.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><p>For On-Prem Cribl instances:</p><ul><li>Username</li><li>Password</li><li>Server URL</li></ul><p>For Cribl.Cloud:</p><ul><li>Client ID</li><li>Client Secret</li><li>Server URL</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the permissions chosen when generating the above inputs.</td></tr></tbody></table>

> **TIP:** Check out the [Cribl Authentication Guide](https://explained.tines.com/en/articles/8445527-cribl-authentication-guide) for more details on how to authenticate.

#### Criminal IP

[Criminal IP](https://www.criminalip.io/) is a cybersecurity platform that provides threat intelligence by scanning and analyzing IP addresses, domains, and other internet assets to detect malicious activities and vulnerabilities.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Criminal IP API</td></tr></tbody></table>

> **TIP:** Check out the [Criminal IP Authentication Guide](https://explained.tines.com/en/articles/8030504-criminal-ip-authentication-guide) for more details on how to authenticate.

#### CrowdStrike

[CrowdStrike](https://www.crowdstrike.com/) is a cybersecurity company that offers endpoint protection and threat intelligence solutions. Their platform leverages cloud-based technologies and AI to detect and prevent cybersecurity threats, including malware, ransomware, and advanced persistent threats, aiming to protect organizations from cyber attacks and breaches.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li><li>Base URL for API (can be identified in the API client page where the API client is created).</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the API client.</td></tr></tbody></table>



> **TIP:** Check out the [Crowdstrike Authentication Guide](https://explained.tines.com/en/articles/7950192-crowdstrike-authentication-guide) for more details on how to authenticate.

#### Cyberhaven

[Cyberhaven](https://www.cyberhaven.com/) is a data security platform that uses AI-powered data lineage, content inspection and behavioural signals to prevent data exfiltration, manage insider risk and secure sensitive information across cloud, email, devices and AI tools.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td><span>API key role must be defined with granular access</span></td></tr></tbody></table>

#### Cyera

[Cyera](https://www.cyera.io/) specializes in data security and management, providing tools to discover, classify, and protect data across various environments while ensuring compliance with industry regulations.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Secret key</li></ul></td></tr><tr><td>Scopes</td><td><span>API calls within the confines of the token's roles</span></td></tr></tbody></table>

#### Cyware CTIX

[Cyware CTIX](https://www.cyware.com/products/threat-intelligence-platform-tip) is a threat-intelligence platform that helps security teams ingest, enrich, analyze, correlate and share threat data from many sources to turn raw indicators into actionable intelligence.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Secret Key</li><li>Access ID</li><li>Endpoint URL</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the permissions of the associated user</td></tr></tbody></table>

#### Darktrace

[Darktrace](https://darktrace.com/) is a cybersecurity company that utilizes AI and machine learning to provide autonomous cyber defense solutions, enabling organizations to detect and respond to cyber threats in real-time across their digital environments.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API tokens can be generated on a per-user basis or via Global tokens. This flow supports either and requires the below inputs for both types of tokens:<br><ul><li>Public token</li><li>Private token</li><li>Darktrace domain</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the API token.</td></tr></tbody></table>

> **TIP:** Check out the [Darktrace Authentication Guide](https://explained.tines.com/en/articles/8771759-darktrace-authentication-guide) for more details on how to authenticate.

#### Databricks

[Databricks](https://www.databricks.com/) is a unified data analytics platform that combines data engineering, data science, and business analytics capabilities, enabling organizations to process and analyze large-scale data, build machine learning models, and derive insights from their data across cloud environments.

## Connect flow details

OAuth connect flow

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>The Databricks host, specified as `https://accounts.cloud.databricks.com` for account operations or the target <a href="https://docs.databricks.com/aws/en/workspace/workspace-details#workspace-url">workspace URL</a>, for example `https://dbc-a1b2345c-d6e7.cloud.databricks.com` for workspace operations.</li><li>The Databricks account ID, for Databricks account operations.</li><li>The service principal client ID.</li><li>The service principal secret.</li></ul></td></tr><tr><td>Scopes</td><td><ul><li>Databricks <a href="https://docs.databricks.com/api/account/introduction">account-level CLI commands or REST API requests</a><br>OR</li><li>Databricks <a href="https://docs.databricks.com/api/workspace">workspace-level CLI commands or REST API requests</a></li></ul><p>And <a href="https://docs.databricks.com/aws/en/dev-tools/auth/oauth-m2m?language=Python#step-1-assign-permissions-to-your-service-principal">set permissions</a> on your service principal</p></td></tr></tbody></table>



Personal access token connect flow

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Personal access token</li><li>Workspace URL</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the account generating the access token<ul></ul></td></tr></tbody></table>

#### Datadog

[Datadog](https://www.datadoghq.com/) is a cloud-based monitoring and analytics platform that helps organizations track the performance of their applications, infrastructure, and services in real time.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>Application Key</li><li>Site</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the credential.</td></tr></tbody></table>

> **TIP:** Check out the [Datadog Authentication Guide](https://explained.tines.com/en/articles/8479372-datadog-authentication-guide) for more details on how to authenticate.

#### Drata

[Drata](https://drata.com/) is a compliance automation platform that helps organizations streamline and maintain their security and compliance posture through continuous monitoring, assessment, and remediation.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>Drata region (NA or EU)<span data-pm-slice="1 1 []">The Drata API uses region-specific base URLs. This will save the relevant base URL for your instance.</span></li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the API client.</td></tr></tbody></table>



> **TIP:** Check out the [Drata Authentication Guide](https://explained.tines.com/en/articles/8044647-drata-authentication-guide) for more details on how to authenticate.

#### Dropbox

[Dropbox](https://www.dropbox.com/) is a cloud-based file hosting service that allows users to store, share, and synchronize files across multiple devices, enabling seamless collaboration and access to data from anywhere with an internet connection.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Dropbox</td></tr><tr><td>Scopes</td><td><p>Files and folders scopes:</p><ul><li><em>files.metadata.write</em>: view and edit information about your Dropbox files and folders</li><li><em>files.metadata.read</em>: view information about your Dropbox files and folders</li><li><em>files.content.write</em>: edit content of your Dropbox files and folders</li><li><em>files.content.read</em>: view content of your Dropbox files and folders</li></ul></td></tr></tbody></table>

#### Dynatrace

[Dynatrace](https://www.dynatrace.com/) is an AI-powered software intelligence platform that provides automated monitoring, observability, and security across cloud environments, applications, and infrastructure to help organizations optimize performance, detect anomalies, and resolve issues.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><p>Depends on the environment:</p><p>SaaS:</p><ul><li>Environment ID</li><li>Token</li></ul><p>ActiveGate</p><ul><li>Domain</li><li>Environment ID</li><li>Token</li></ul></td></tr><tr><td>Scopes</td><td>Set at token creation</td></tr></tbody></table>

#### Elastic

[Elastic](https://www.elastic.co/) is a software company that provides a suite of search, analytics, and visualization tools, including the Elasticsearch search engine, which helps organizations store, search, and analyze large volumes of data in real-time.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Application endpoint URL<ul><li>Elasticsearch (required)</li><li>Kibana (required)</li><li>APM (optional)</li><li>Fleet (optional)</li><li>Enterprise search (optional)</li></ul></li></ul></td></tr><tr><td>Scopes</td><td>Privileges specified by user creating the API key</td></tr></tbody></table>

> **TIP:** Check out the [Elastic Authentication Guide](https://explained.tines.com/en/articles/8507578-elastic-security-authentication-guide) for more details on how to authenticate.

#### EmailRep

[EmailRep](https://emailrep.io/) is a tool and API used for email-based risk assessment and reputation scoring, offering insights into the trustworthiness and potential risk associated with email addresses by analyzing various factors such as past behavior, domain reputation, and metadata.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the EmailRep API</td></tr></tbody></table>

> **TIP:** Check out the [EmailRep Authentication Guide](https://explained.tines.com/en/articles/8044733-emailrep-authentication-guide) for more details on how to authenticate.

#### Entro Security

[Entro Security](https://entro.security/) is a platform that helps organizations manage and secure their digital identities and access permissions across their technology stack.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>API endpoint</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Entro Security API</td></tr></tbody></table>

#### F5 Distributed Cloud Services

[F5 Distributed Cloud Services](https://www.f5.com/products/distributed-cloud-services) is a SaaS-based platform that enables organizations to deploy, secure and manage applications across data centres, multi-cloud and edge environments with a unified policy engine and management console.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><p>API Token</p><p>Domain</p></td></tr><tr><td>Scopes</td><td>Determined by the permissions of the associated user.</td></tr></tbody></table>

#### Feedly

[Feedly](https://feedly.com/) is a news aggregator application that allows users to organize, read, and share content from their favorite websites, blogs, and news sources in one centralized platform.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Feedly API</td></tr></tbody></table>

> **TIP:** Check out the [Feedly Authentication Guide](https://explained.tines.com/en/articles/8472729-feedly-authentication-guide) for more details on how to authenticate.

#### Figma

[Figma](https://www.figma.com/) is a cloud-based design and prototyping tool that allows teams to collaborate in real-time on user interface (UI) and user experience (UX) design projects, streamlining the design process and facilitating efficient iteration and feedback loops.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Figma</td></tr><tr><td>Scopes</td><td><ul><li><em>files:read</em>: read files, projects, users, versions, comments, components &amp; styles, and webhooks.</li><li><em>file_comments:write</em>: post and delete comments and comment reactions in files.</li></ul></td></tr></tbody></table>

#### Flare.io

[Flare.io](http://flare.io/) is a cloud-based financial management platform designed to help startups and growing businesses manage their cash flow, financial planning, and fundraising processes more efficiently.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Permissions are tied to the account the API key is generated for.</td></tr></tbody></table>

#### Freshdesk

[Freshdesk](https://www.freshworks.com/freshdesk/) is a cloud-based customer support software that helps businesses manage and streamline their customer service operations through ticketing, automation, and multi-channel support.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Access varies based on the permissions available to your Freshdesk user profile.</td></tr></tbody></table>

#### Freshservice

[Freshservice](https://www.freshworks.com/freshservice/) is a cloud-based, AI-powered IT service management (ITSM) and enterprise service management (ESM) platform designed to unify and automate ticketing, incident and change management, asset tracking, and self-service workflows across IT and non-IT teams.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Access depends on the permissions available to your Freshservice user profile</td></tr></tbody></table>

> **TIP:** Check out the [Freshservice Authentication Guide](https://explained.tines.com/en/articles/8978677-freshservice-authentication-guide) for more details on how to authenticate.

#### GitGuardian

[GitGuardian](https://www.gitguardian.com/) is a cybersecurity platform that specializes in detecting and preventing secrets (such as API keys and passwords) from being exposed in source code repositories.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key (personal access token)</td></tr><tr><td>Scopes</td><td>Defined by the user generating the token. Only API calls with the "scan" scope count toward the quota.</td></tr></tbody></table>

> **TIP:** Check out the [GitGuardian Authentication Guide](https://explained.tines.com/en/articles/8592157-gitguardian-authentication-guide) for more details on how to authenticate.

#### GitHub

[GitHub](https://github.com/) is a web-based platform that provides version control and collaboration tools for software developers, allowing them to host, review, and manage code repositories, track changes, and work together on projects efficiently.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client Secret</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the OAuth app.</td></tr></tbody></table>

#### GitLab

[GitLab](https://about.gitlab.com/) is a web-based DevOps lifecycle tool that provides a Git repository manager providing wiki, issue-tracking, and CI/CD pipeline features, enabling teams to collaborate on code development, automate software delivery, and manage the entire software development lifecycle in a single platform.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in GitLab</td></tr><tr><td>Scopes</td><td><ul><li><em>api</em>: access the authenticated user's API</li><li><em>read_api</em>: read API</li><li><em>read_user</em>: read the authenticated user's personal information</li><li><em>read_repository</em>: allows read-only access to the repository</li></ul></td></tr></tbody></table>

#### Google Calendar

[Google Calendar](https://workspace.google.com/products/calendar/) is a time-management and scheduling tool that enables users to organize their events, appointments, and meetings, set reminders, and share calendars with others, facilitating efficient time planning and coordination across teams and individuals.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Google Calendar</td></tr><tr><td>Scopes</td><td><em>https://www.googleapis.com/auth/calendar</em>: <span>See, edit, share, and permanently delete all the calendars you can access using Google Calendar.</span><em></em></td></tr></tbody></table>

#### Google Docs

[Google Docs](https://workspace.google.com/products/docs/) is a cloud-based word processing application that allows users to create, edit, and collaborate on documents in real-time, enabling seamless sharing and simultaneous editing among multiple users, enhancing productivity and facilitating remote teamwork.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Google Docs</td></tr><tr><td>Scopes</td><td><em>https://www.googleapis.com/auth/documents</em>: See, edit, create, and delete all your Google Docs documents</td></tr></tbody></table>



> **NOTE:** This connect flow will work with the [Google Docs API](https://developers.google.com/docs/api/reference/rest), which is different from the [Google Drive API](https://developers.google.com/drive/api/reference/rest/v3). Attempts to use this credential with calls to the Google Drive API will not have sufficient permissions.

#### Google Service Account

A [Google service account](https://cloud.google.com/iam/docs/service-account-overview) is a special kind of account used by an application, rather than a person. You can use a service account to access data or perform actions by the robot account, or to access data on behalf of Google Workspace or Cloud Identity users. 

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request that includes a JWT token request.</td></tr><tr><td>Required inputs</td><td><ul><li><a href="https://developers.google.com/workspace/guides/create-credentials#create_credentials_for_a_service_account">Private key JSON file</a> of service account</li><li>Email of the user to impersonate: For example, if you wanted to access a user named John's emails, you would enter John's email address here. <span>If you are not accessing a particular user's account, enter the service account email address. </span>You can change this later as needed.</li><li><a href="https://developers.google.com/identity/protocols/oauth2/scopes">Scopes</a>: a space separated list of oauth2 scopes enabled for the service account on the <a href="https://developers.google.com/workspace/guides/create-credentials#optional_set_up_domain-wide_delegation_for_a_service_account">Domain-wide delegation page</a>.</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user managing the service account in your organization. Scopes that can be enabled are listed <a href="https://developers.google.com/identity/protocols/oauth2/scopes">here</a>.</td></tr></tbody></table>

> **TIP:** Service accounts for use with Google Workspace are popular among Tines users. For further details on scopes and setup for a service account related to Google Workspace, see the [Google Workspace Authentication Guide](https://explained.tines.com/en/articles/7969948-google-workspace-authentication-guide-jwt).

#### Google Sheets

[Google Sheets](https://www.google.com/sheets/about/) is a cloud-based spreadsheet application that allows users to create, edit, and collaborate on spreadsheets in real-time.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Google Sheets</td></tr><tr><td>Scopes</td><td><em>https://www.googleapis.com/auth/spreadsheets</em>: See, edit, create, and delete all your Google Sheets spreadsheets</td></tr></tbody></table>

#### Greenhouse

[Greenhouse](https://www.greenhouse.com/) is a talent acquisition platform that helps organizations streamline and optimize their hiring processes. It offers features such as applicant tracking, job posting, interview scheduling, candidate evaluation, and reporting, enabling recruiters and hiring managers to attract, assess, and hire top talent more efficiently and effectively. 

This connect flow creates a credential for use with the [candidate ingestion API](https://developers.greenhouse.io/candidate-ingestion.html#introduction) in Greenhouse.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Greenhouse</td></tr><tr><td>Scopes</td><td><ul><li><em>candidates.create</em>: Create new candidates or prospects</li><li><em>candidates.view</em>: View candidates imported via this partner</li><li><em>jobs.view</em>: View my jobs</li></ul></td></tr></tbody></table>

#### GreyNoise

[GreyNoise](https://www.greynoise.io/) is a cybersecurity company that collects and analyzes internet background noise, helping organizations identify and filter out non-targeted, benign activity from their network traffic, thus enabling more effective threat detection and response efforts.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the GreyNoise API</td></tr></tbody></table>

> **TIP:** Check out the [GreyNoise Authentication Guide](https://explained.tines.com/en/articles/8393787-greynoise-authentication-guide) for more details on how to authenticate.

#### Hashicorp Terraform

[HashiCorp Terraform](https://www.terraform.io/) is an open-source infrastructure as code (IaC) tool that enables users to define, provision, and manage cloud infrastructure resources across multiple providers using a declarative configuration language.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Determined by the token type chosen. See <a href="https://developer.hashicorp.com/terraform/cloud-docs/api-docs#authentication">docs</a> for further details.</td></tr></tbody></table>

#### Have I Been Pwned

[Have I Been Pwned](https://haveibeenpwned.com/) is a free online service that allows users to check if their email addresses or passwords have been compromised in known data breaches, helping individuals assess their online security and take appropriate actions to protect their accounts.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td><a href="https://haveibeenpwned.com/API/v3#Authorisation">Authorization</a> is required for all APIs that enable searching HIBP by email address or domain, namely retrieving all breaches for an account, retrieving all pastes for an account, retrieving all breached email addresses for a domain and retrieving all stealer log domains for a breached email addresses. Only the Pwned Passwords API is free and does not require authorization.</td></tr></tbody></table>

> **TIP:** Check out the [Have I Been Pwned Authentication Guide](https://explained.tines.com/en/articles/8472679-have-i-been-pwned-authentication-guide) for more details on how to authenticate.

#### HiBob

[HiBob](https://www.hibob.com/) is a HR platform designed to streamline the entire employee lifecycle while fostering a social, culture-driven workplace.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Token</li><li>ID</li></ul></td></tr><tr><td>Scopes</td><td><p>Access depends on the permissions assigned.</p></td></tr></tbody></table>

#### HubSpot

[HubSpot](https://www.hubspot.com/) is an all-in-one inbound marketing, sales, and customer relationship management (CRM) platform. It provides tools and features for businesses to attract visitors, convert leads, and close customers. 

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in HubSpot</td></tr><tr><td>Scopes</td><td>The app requests the below scopes for all accounts. You can request additional desired custom scopes, which will be enabled if your HubSpot account has the required account tier needed for that permission. To view all HubSpot scopes, see their <a href="https://developers.hubspot.com/docs/api/working-with-oauth#scopes" target="_blank" rel="noopener">documentation</a>. &nbsp; &nbsp; &nbsp;<ul><ul><li><em>oauth: </em>Basic scope required for OAuth. This scope is added by default to all apps.</li><li><em>forms</em>: This includes access to the Forms endpoints. Works with Forms endpoints.</li><li><em>tickets</em>: <span>This includes access to tickets. Works with the Tickets endpoints.</span></li><li><em>e-commerce</em>: <span>This includes access to e-commerce features. Works with Products and line items endpoints. Only <em>Professional</em> and <em>Enterprise</em> accounts can use this scope for the Products API.</span></li><li><em>sales-email-read</em>: Grants access to read all details of one-to-one emails sent to contacts. Works with engagements endpoints. This scope is required to get the content of email engagements.</li><li><em>forms-uploaded-files</em>: Download files submitted through a form. Allows users to get a file uploaded via form submission endpoint.</li><li><em>crm.objects.contacts.read</em>: <span>View properties and other details about contacts. Works with the Contacts endpoints.</span></li></ul></ul><span style="background-color: transparent; color: var(--color); font-family: inherit; font-size: inherit; font-style: inherit; font-variant-ligatures: inherit; font-variant-caps: inherit; font-weight: inherit; letter-spacing: 0px;"></span></td></tr></tbody></table>

#### Hybrid Analysis

[Hybrid Analysis](https://www.hybrid-analysis.com/) is an online malware analysis service that combines automated and manual analysis techniques to provide detailed reports on the behavior and characteristics of submitted files or URLs, helping security professionals detect and understand potential threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Defined by the user generating the API token.</td></tr></tbody></table>

> **TIP:** Check out the [Hybrid Analysis Authentication Guide](https://explained.tines.com/en/articles/8643815-hybrid-analysis-authentication-guide) for more details on how to authenticate.

#### Hyperproof

[Hyperproof](https://hyperproof.io/) is a cloud-based platform designed to help organizations manage their security assurance and compliance work on a continuous basis. 

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID for your API client</li><li>Client secret for your API client</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user for their API client in Hyperproof</td></tr></tbody></table>

#### InfoBlox BloxOne

[Infoblox BloxOne](https://www.infoblox.com/) is a cloud-native network services platform that provides unified management for DNS, DHCP, and IP address management (IPAM) across hybrid and multi-cloud environments, enhancing network reliability, security, and automation.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Defined by the user generating the API token</td></tr></tbody></table>

> **TIP:** Check out the [InfoBlox BloxOne Authentication Guide](https://explained.tines.com/en/articles/8820372-infoblox-bloxone-authentication-guide) for more details on how to authenticate.

#### Infoblox NIOS

[Infoblox NIOS](https://www.infoblox.com/products/nios/) (Network Identity Operating System) is a core network services platform that provides integrated DNS, DHCP, and IP address management (IPAM) capabilities, often referred to as DDI, to help organizations manage and secure their network infrastructure more efficiently.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Username</li><li>Password</li><li>Base URL</li><li>WAPI version</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Infoblox NIOS API</td></tr></tbody></table>

#### Intercom

[Intercom](https://www.intercom.com/) is a customer messaging platform that enables businesses to communicate with their customers across various channels, including live chat, email, and in-app messages.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Intercom</td></tr><tr><td>Scopes</td><td><ul><li><em>Read and list users and companies</em></li><li><em>Write users and companies</em><em></em></li><li><em>Read and write users</em></li><li><em>Read one user and one company</em></li><li><em>Read events</em></li><li><em>Write events</em></li><li><em>Read conversations</em></li><li><em>Write conversations</em></li><li><em>Read tags</em></li><li><em>Write tags</em></li><li><em>Read counts</em></li><li><em>Export message data</em></li><li><em>Export content data</em></li><li><em>Read content data</em></li><li>Read admins</li><li><em>Update admins</em></li><li><em>Read one admin</em></li><li><em>Read admin activity logs</em></li><li><em>Read and list articles</em></li><li><em>Read and write articles</em></li><li><em>Create phone call redirects</em></li><li><em>Read and list news items and newsfeeds</em></li><li><em>Read and write news items and newsfeeds</em></li></ul></td></tr></tbody></table>

#### IPinfo

[IPinfo](https://ipinfo.io/) is an IP geolocation and network data provider offering information on IP address origins, including city and country, ISP, ASN, and VPN/proxy usage. It supports applications in security, content personalization, and analytics.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the IPinfo API depending on plan</td></tr></tbody></table>

> **TIP:** Check out the [IPinfo Authentication Guide](https://explained.tines.com/en/articles/8385084-ipinfo-authentication-guide) for more details on how to authenticate.

#### IPQualityScore

[IPQualityScore](https://www.ipqualityscore.com/) is a fraud prevention service that analyzes IP addresses, emails, and other data points to detect and block fraudulent activities, assess risk, and ensure the quality of online traffic.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the IPQualityScore API depending on plan</td></tr></tbody></table>

> **TIP:** Check out the [IPQualityScore Authentication Guide](https://explained.tines.com/en/articles/8454978-ipqualityscore-authentication-guide) for more details on how to authenticate.

#### IPstack

[IPstack](https://ipstack.com/) offers the leading IP to geolocation APIs and global IP database services worldwide.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to different endpoints in the IPstack API depending on IPstack subscription plan</td></tr></tbody></table>

#### IP2Location

[P2Location](https://www.ip2location.com/) is a non-intrusive IP location lookup technology that retrieves geolocation information with no explicit permission required from users.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the IP2Location API depending on subscription.</td></tr></tbody></table>

#### Iru

[Iru](https://www.iru.com/), formally Kandji, is an AI-powered IT and security software company that provides a unified platform for identity and access management, endpoint security and device management, and compliance automation to help organizations secure their users, apps and devices.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>API URL</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the API token</td></tr></tbody></table>

#### Jamf Pro

[Jamf](https://www.jamf.com/) is a software platform that specializes in Apple device management, providing tools for deploying, updating, and securing Mac computers, iPads, iPhones, and Apple TVs in enterprise environments.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li><li>Jamf domain</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Jamf Pro API</td></tr></tbody></table>



> **TIP:** Check out the [Jamf Authentication Guide](https://explained.tines.com/en/articles/8216965-jamf-authentication-guide) for more details on how to authenticate.

#### Jeli

[Jeli](https://www.pagerduty.com/platform/jeli/) helps organizations respond to, manage, and analyze incidents in order to build more resilient infrastructure and teams.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API access token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Jeli API</td></tr></tbody></table>

#### Jina AI

[Jina AI](https://jina.ai/) is a search AI company, offering a platform that combines Embeddings, Rerankers, and Small Language Models. It provides cloud-native neural search solutions for enterprises and developers, enabling the creation of powerful GenAI and multimodal search applications across various industries, including e-commerce, enterprise search, and cloud computing.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Jina AI API</td></tr></tbody></table>

#### Jira Service Management

[Jira Service Management](https://www.atlassian.com/software/jira/service-management) is a service desk platform developed by Atlassian. It is used by IT, operations, and business teams to manage service requests, track incidents, and coordinate work across departments. The tool integrates with other Atlassian products and supports customizable workflows.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Account email associated with the API key</li><li>Jira domain</li></ul></td></tr><tr><td>Scopes</td><td>The API key generated is limited to the permissions set on the user profile that generated the key. Customers often create an account for Tines to associate the API key with, scoping the permissions for that user as desired.</td></tr></tbody></table>

#### Jira Software

[Jira Software](https://www.atlassian.com/software/jira) is a project management tool developed by Atlassian, primarily used by software development teams to plan, track, and manage their work throughout the software development lifecycle.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Account email associated with the API key</li><li>Jira domain</li></ul></td></tr><tr><td>Scopes</td><td>The API key generated is limited to the permissions set on the user profile that generated the key. Customers often create an account for Tines to associate the API key with, scoping the permissions for that user as desired.</td></tr></tbody></table>



> **TIP:** Check out the [Jira Software Authentication Guide](https://explained.tines.com/en/articles/8776820-jira-authentication-guide) for more details on how to authenticate.

#### Joe Sandbox

[Joe Sandbox](https://www.joesandbox.com/) is a malware analysis platform that automatically analyzes files, URLs, and threats in a secure, isolated environment to detect malicious behavior and generate detailed reports.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Joe Sandbox API</td></tr></tbody></table>

> **TIP:** Check out the [Joe Sandbox Authentication Guide](https://explained.tines.com/en/articles/8458932-joe-sandbox-authentication-guide) for more details on how to authenticate.

#### JumpCloud

[JumpCloud](https://jumpcloud.com/) is a cloud-based directory platform that provides identity and access management services for organizations, allowing them to securely connect users to various IT resources.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the JumpCloud API</td></tr></tbody></table>

> **TIP:** Check out the [JumpCloud Authentication Guide](https://explained.tines.com/en/articles/8596959-jumpcloud-authentication-guide) for more details on how to authenticate.

#### JupiterOne

[JupiterOne](https://www.jupiterone.com/) is a cyber asset management and governance platform that helps organizations map, monitor, and secure all their digital assets, relationships, and risks across cloud and enterprise environments.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>Account ID</li></ul></td></tr><tr><td>Scopes</td><td>Scopes are determined by the user creating the API token</td></tr></tbody></table>

> **TIP:** Check out the [JupiterOne Authentication Guide](https://explained.tines.com/en/articles/8455011-jupiterone-authentication-guide) for more details on how to authenticate.

#### Klaviyo

[Klaviyo](https://www.klaviyo.com/) is a data-driven marketing automation platform designed specifically for e-commerce brands to power email and SMS campaigns that turn customer insights into revenue.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li></ul></td></tr><tr><td>Scopes</td><td><p>Access depends on the type of API key selected and the permissions available to your Klaviyo user profile.</p></td></tr></tbody></table>

#### Lark

[Lark](https://www.larksuite.com/) by ByteDance is a comprehensive workplace collaboration platform that integrates messaging, videoconferencing, calendar, document collaboration, and workflow management tools into a single unified interface, designed to enhance team productivity and communication across organizations.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>App ID</li><li>App secret</li></ul></td></tr><tr><td>Scopes</td><td>Determined by creator of the app</td></tr></tbody></table>

#### Lumos

[Lumos](https://www.lumos.com/) is an AI-powered data discovery and governance platform that helps organizations manage, secure, and optimize their data assets across various systems and applications. 


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Defined by the user generating the token</td></tr></tbody></table>

#### Mailchimp

[Mailchimp](https://mailchimp.com/) is an all-in-one marketing platform that allows businesses to create, send, and analyze email marketing campaigns.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Mailchimp</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Mailchimp marketing API</td></tr></tbody></table>

#### Marketo Engage

[Marketo Engage](https://business.adobe.com/products/marketo.html) is a full-featured marketing automation platform that enables companies to manage leads, run personalized multi-channel campaigns, nurture prospects through the buying journey and measure marketing ROI.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>At various steps you will be prompted to enter the following:<br><ul><li>Account ID</li><li>Client ID</li><li>Client Secret</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the permissions of the associated user</td></tr></tbody></table>

#### Material Security

[Material Security](https://material.security/) is a cybersecurity company that secures the cloud office with a unified suite of email security, user behavior analytics, and data loss prevention purpose-built for Microsoft 365 & Google Workspace. 

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><ul><li>API key</li><li>Domain</li></ul></ul></td></tr><tr><td>Scopes</td><td>The API key generated is limited to the permissions set on the user profile that generated the key. Customers often create an account for Tines to associate the API key with, scoping the permissions for that user as desired.</td></tr></tbody></table>

#### Microsoft Defender for Endpoint

[Microsoft Defender for Endpoint](https://www.microsoft.com/en-us/security/business/endpoint-security/microsoft-defender-endpoint) is a cloud-based security solution that provides advanced protection against threats by detecting, investigating, and responding to attacks across endpoints through behavioral sensors, analytics, and threat intelligence.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:<br><ul><li>Application (client) ID</li><li>Directory (tenant) ID</li><li>Client secret value</li><li>Deployment environment (global or US gov)</li></ul></td></tr><tr><td>Scopes</td><td><p>This connect flow supports <em>Application</em> type permissions. With this type of permission, the<span data-pm-slice="1 1 []"> app will perform actions as itself rather than impersonating your signed-in user. Do not select <em>delegated</em> permissions.</span></p><p>All permissions are determined by the user managing the application.</p></td></tr></tbody></table>



> **NOTE:** Depending on the permissions chosen and your organizational settings, you may need to obtain admin approval for your app's permissions. Please note the app will not work for actions requiring these permissions until they are granted by your admin

#### Microsoft Graph

[Microsoft Graph](https://developer.microsoft.com/en-us/graph) is a unified RESTful programming interface provided by Microsoft. It enables developers to access a wide range of data and functionality across Microsoft services, such as Office 365, Azure Active Directory, OneDrive, Outlook, SharePoint, Teams, and more.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:<br><ul><li>Application (client) ID</li><li>Directory (tenant) ID</li><li>Client secret value</li><li>Deployment environment</li></ul></td></tr><tr><td>Scopes</td><td><p>This connect flow supports <em>Application</em> type permissions. With this type of permission, the<span data-pm-slice="1 1 []"> app will perform actions as itself rather than impersonating your signed-in user. Do not select <em>delegated</em> permissions.</span></p><p>All permissions are determined by the user managing the application.</p></td></tr></tbody></table>



> **NOTE:** Depending on the permissions chosen and your organizational settings, you may need to obtain admin approval for your app's permissions. Please note the app will not work for actions requiring these permissions until they are granted by your admin

#### Microsoft OneDrive

[Microsoft OneDrive](https://www.microsoft.com/en-us/microsoft-365/onedrive/online-cloud-storage) is a cloud storage service that allows users to store files and documents securely online, and access them from any device with an internet connection.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Microsoft OneDrive</td></tr><tr><td>Scopes</td><td><ul><li><em>offline_access</em>: Maintain access to data you have given it access to</li><li><em>User.Read</em>: Sign in and read user profile</li><li><em>Files.ReadWrite</em>: Have full access to user files</li><li><em>Files.ReadWrite.All</em>: Have full access to all files user can access<em></em></li></ul></td></tr></tbody></table>

#### Microsoft Outlook

[Microsoft Outlook](https://www.microsoft.com/en-us/microsoft-365/outlook/email-and-calendar-software-microsoft-outlook) is an email client and personal information manager that allows users to send, receive, and organize emails, as well as manage calendars, contacts, tasks, and notes.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Microsoft Outlook</td></tr><tr><td>Scopes</td><td><ul><li><em>offline_access</em>: Maintain access to data you have given it access to</li><li><em>User.Read</em>: Sign in and read user profile</li><li><i>Calendars.ReadWrite</i>: Have full access to user calendars</li><li><em>Calendars.ReadWrite.Shared</em>: Read and write user and shared calendars</li><li><em>Contacts.ReadWrite</em>: Have full access to user contacts</li><li><em>Mail.ReadWrite</em>: Read and write access to user mail</li><li><em>Mail.ReadWrite.Shared</em>: Read and write user and shared mail</li><li><em>Mail.Send</em>: Send mail as a user</li><li><em>Mail.Send.Shared</em>: Send mail on behalf of others</li></ul></td></tr></tbody></table>

#### Microsoft Teams

[Microsoft Teams](https://www.microsoft.com/en-us/microsoft-teams/group-chat-software) is a collaboration platform that combines workplace chat, video meetings, file storage, and application integration.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Microsoft Teams</td></tr><tr><td>Scopes</td><td><ul><li><em>offline_access</em>: Maintain access to data you have given it access to</li><li><em>User.Read</em>: Sign in and read user profile</li><li><em>Channel.ReadBasic.All</em>: Read the names and descriptions of channels</li><li><em>ChannelMessage.Read.All</em>: Read user channel messages</li><li><em>ChannelMessage.Send</em>: Send channel messages</li><li><em>Chat.ReadWrite</em>: Read and write user chat messages</li><li><em>Team.ReadBasic.All</em>: Read the names and descriptions of teams</li><li><em>TeamsAppInstallation.ReadForUser</em>: Read user's installed Teams apps</li></ul></td></tr></tbody></table>

> **TIP:** Check out the [Microsoft Teams Authentication Guide](https://explained.tines.com/en/articles/8677501-microsoft-teams-authentication-guide) for more details on how to install the app.

#### Mimecast

[Mimecast](https://www.mimecast.com/) is a cloud‑based cybersecurity company that provides AI‑powered email security, archiving, continuity, compliance, insider‑risk management, and human‑risk training to protect organizations from threats like phishing, malware, BEC, and data loss

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Mimecast 2.0 API</td></tr></tbody></table>

#### MISP

[MISP](https://www.misp-project.org/) (Malware Information Sharing Platform & Threat Sharing) is an open-source threat intelligence platform that enables organizations to share, store, and correlate indicators of compromise of targeted attacks, threat intelligence, financial fraud information, vulnerability information, and even counter-terrorism information.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>Authentication key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the MISP API</td></tr></tbody></table>

> **TIP:** Check out the [MISP Authentication Guide](https://explained.tines.com/en/articles/8617354-misp-authentication-guide) for more details on how to authenticate.

#### Monday.com

[Monday.com](https://monday.com/) is a cloud-based work management platform that helps teams plan, track and manage projects, workflows and collaboration with customizable, no-code tools and AI-powered features.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>Account domain</li></ul></td></tr><tr><td>Scopes</td><td>Personal tokens mirror all permission levels set in the monday.com UI, including board, column, item or acount access.</td></tr></tbody></table>

#### NetBox

[NetBox](https://netboxlabs.com/products/netbox/) is the world’s most popular platform for understanding, operating, automating, and securing networks by acting as a centralized, authoritative infrastructure “source of truth” to replace fragmented data and drive efficient operations across networks and data centers

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>URL</li></ul></td></tr><tr><td>Scopes</td><td><p>Defined by the user generating the API token</p></td></tr></tbody></table>

#### Netlas

[Netlas](https://netlas.io/) is a cybersecurity reconnaissance platform that scans and indexes internet-connected devices, services, and infrastructure to help organizations discover and monitor their external attack surface.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Netlas API</td></tr></tbody></table>

#### New Relic

[New Relic](https://newrelic.com/) is a cloud-based observability platform that provides real-time monitoring, analytics, and troubleshooting capabilities for applications, infrastructure, and user experiences to help organizations improve their software performance and reliability.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the New Relic API</td></tr></tbody></table>

> **TIP:** Check out the [New Relic Authentication Guide](https://explained.tines.com/en/articles/8462818-new-relic-authentication-guide) for more details on how to authenticate.

#### Netsuite

[NetSuite](https://www.netsuite.com/portal/home.shtml) is a cloud-based, AI-powered business management suite that enables organizations to unify ERP, CRM, e-commerce and analytics into a single platform.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of a credential. At various steps you will be prompted to enter the following:<br><ul><li>Account ID</li><li>Consumer Key</li><li>Consumer Secret</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td><p>Defined by the user generating the Consumer Key and Consumer Secret in the NetSuite product</p></td></tr></tbody></table>

#### NinjaOne

[NinjaOne](https://www.ninjaone.com/) is a cloud-native unified IT operations platform that helps IT teams manage, secure, automate and support all endpoints from a single console.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user creating the application.</td></tr></tbody></table>

#### Notion

[Notion](https://www.notion.so/) is an all-in-one workspace tool that combines note-taking, task management, and collaboration features in a single platform.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Notion</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the <a href="https://developers.notion.com/reference/intro">Notion API</a>.</td></tr></tbody></table>

#### Okta

[Okta](https://www.okta.com/) is an identity and access management platform that provides secure authentication and authorization solutions for businesses.

## Connect flow details



OAuth connect flow

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Okta Organization</li><li>Client ID</li><li>Public Key</li><li>Private Key</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td>Granted in the application. Note: Only a super admin has permission to grant scopes to an app.</td></tr></tbody></table>



API key connect flow

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Okta API token</li><li>Okta domain</li></ul></td></tr><tr><td>Scopes</td><td>The API key generated is limited to the permissions set on the user profile that generated the key. Customers often create an account for Tines to associate the API key with, scoping the permissions for that user as desired.</td></tr></tbody></table>



> **TIP:** Check out the [Okta Authentication Guide](https://explained.tines.com/en/articles/7967540-okta-authentication-guide) for more details on how to authenticate.

#### Ollama

[Ollama](https://ollama.com/) is a platform that lets you run, automate and manage large language models locally or in the cloud with a focus on developer control and privacy.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the permissions of the associated user</td></tr></tbody></table>

#### OneLogin

[OneLogin](https://www.onelogin.com/) is an identity and access management (IAM) platform that provides secure, single sign-on access to cloud and on-premises applications for enterprises and their users.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client Secret</li><li>Subdomain</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the credential</td></tr></tbody></table>

> **TIP:** Check out the [OneLogin Authentication Guide](https://explained.tines.com/en/articles/8468486-onelogin-authentication-guide) for more details on how to authenticate.

#### Onetime Secret

[OneTime Secret](https://onetimesecret.com/) is a web-based service that allows users to share sensitive information securely by creating self-destructing messages or links that can only be viewed once.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>Email</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Onetime Secret API</td></tr></tbody></table>

#### OneTrust

[OneTrust](https://www.onetrust.com/) is a comprehensive privacy management and data governance platform that helps organizations automate compliance with global privacy regulations while enabling responsible data use across their operations. It provides centralized tools for privacy risk assessments, data mapping, and privacy rights management, allowing businesses to both meet regulatory requirements and build trust with customers through transparent data practices.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Domain</li></ul><p>or</p><ul><li>Access token (if using client credentials)</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Determined by user creating the API key or access token</td></tr></tbody></table>

#### Oomnitza

[Oomnitza](https://www.oomnitza.com/) is an Enterprise Technology Management platform that provides organizations with a unified system to track, manage, and automate the complete lifecycle of all their technology assets. It integrates with numerous existing tools to consolidate technology management across departments, enabling better visibility, control, and cost optimization of IT resources from purchase through end-of-life.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Oomnitza URL</li></ul></td></tr><tr><td>Scopes</td><td>Aligned with the scopes of the account the API key is created within</td></tr></tbody></table>

#### OpenAI

[OpenAI](https://openai.com/) provides access to advanced AI technologies through APIs and partnerships, enabling developers and businesses to integrate powerful natural language processing, text generation, and other AI capabilities into their products and services.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>OpenAI Project API key</td></tr><tr><td>Scopes</td><td>Defined by the user generating the API key.</td></tr></tbody></table>

#### Opsgenie

[OpsGenie](https://www.atlassian.com/software/opsgenie) is an incident management and alerting platform that helps organizations manage their on-call schedules, route alerts to the right teams, and coordinate incident response processes efficiently.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>If your instance is EU based (yes or no input)</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the API key</td></tr></tbody></table>

#### Orca Security

[Orca Security](https://orca.security/) provides agentless cloud security and compliance solutions that offer comprehensive visibility and protection for cloud environments without the need for installing agents on individual assets.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>Base URL</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user generating the API token</td></tr></tbody></table>

> **TIP:** Check out the [Orca Security Authentication Guide](https://explained.tines.com/en/articles/8227139-orca-security-authentication-guide) for more details on how to authenticate.

#### PagerDuty

[PagerDuty](https://www.pagerduty.com/) is a digital operations management platform that helps businesses improve incident response by centralizing alerts, facilitating on-call scheduling, and providing analytics for continuous improvement.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API Key</td></tr><tr><td>Scopes</td><td>Defined by the user generating the API key.</td></tr></tbody></table>



> **NOTE:**
> This flow creates a PagerDuty credential for the [REST API](https://developer.pagerduty.com/docs/531092d4c6658-rest-api-v2-overview). The REST API is used for accessing and manipulating data concerning all of the entities on a PagerDuty account.
> 
> The REST API should not be used to handle alert events and change events. - for that, use the [Events API](https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgw-events-api-v2-overview) (and corresponding connect flow) instead.

#### PagerDuty Events API

[PagerDuty](https://www.pagerduty.com/) is a digital operations management platform that helps businesses improve incident response by centralizing alerts, facilitating on-call scheduling, and providing analytics for continuous improvement.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>Within your selected service, an integration key for the Events API V2 integration.</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the PagerDuty Events API</td></tr></tbody></table>



> **NOTE:**
> This flow creates a PagerDuty credential for the [Events API](https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgw-events-api-v2-overview), which is used to handle alert events and change events.
> 
> The Events API should not be used for accessing and manipulating data concerning all of the entities on a PagerDuty account - for that, use the [REST API](https://developer.pagerduty.com/docs/531092d4c6658-rest-api-v2-overview) instead.

#### Panther

[Panther](https://panther.com/) is a cloud-native security analytics platform that helps organizations detect and respond to threats by analyzing log data from various sources using real-time processing and customizable detection rules.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>API URL</li></ul></td></tr><tr><td>Scopes</td><td>Defined by user generating the API token</td></tr></tbody></table>

#### Personio

[Personio](https://www.personio.com/) is a cloud-based HR management and recruiting platform that streamlines HR processes, including employee data management, time tracking, payroll, and applicant tracking for small to medium-sized businesses.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>API Secret</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the API client and secret</td></tr></tbody></table>

#### Pinecone

[Pinecone](https://www.pinecone.io/) is a vector database that enables fast and scalable similarity search for applications using machine learning and AI.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API Key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Pinecone API</td></tr></tbody></table>

#### Pipedrive

[Pipedrive](https://www.pipedrive.com/) is a CRM platform designed to streamline lead management and activity-based selling through an intuitive, visual pipeline that helps teams focus on the right actions to close deals faster.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Token</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td><p>Access depends on the permissions available to your Pipedrive user profile.</p></td></tr></tbody></table>

#### Productboard

[Productboard](https://www.productboard.com/) is a product management platform that helps product managers and teams prioritize, plan, and develop their products effectively.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Productboard</td></tr><tr><td>Scopes</td><td><p>Some Productboard user roles are allowed to authorize only a subset of the OAuth2 scopes. For a Productboard user to be able to authorize an OAuth2 application, their role must be allowed to authorize all the scopes the application is requesting. Some of the below scopes require admin authorization.</p><ul><li><em>custom_fields:read</em>: <span>The application will be able to read hierarchy entity custom field definitions.</span></li><li><em>members_pii:read</em>: <span>The application will be able to read personally identifying information (names, email addresses, etc.) from your Productboard users.</span></li><li><em>notes:create</em>: <span>The application will be able to create notes.</span></li><li><em>notes:read</em>: <span>The application will be able to read notes.</span></li><li><em>companies:read</em>: <span>The application will be able to read companies.</span></li><li><em>plugin_integrations:manage</em>: <span>The application will be able to introduce and manage a UI plugin on your Productboard space.</span></li><li><em>product_hierarchy_data:create</em>: <span>The application will be able to create all features and attributes in your workspace. This includes releases, roadmaps, and features.</span></li><li><em>product_hierarchy_data:manage</em>: <span>The application will be able to edit and delete features and attributes in your workspace. This includes releases, roadmaps, and features.</span></li><li><em>product_hierarchy_data:read</em>: <span>The application will be able to read all hierarchy entities (products, components, and features) and their attributes in your workspace. This includes the relationships between features, custom field values, releases, and Jira metadata.</span></li><li><em>releases:create</em>: <span>The application will be able to create releases and release groups.</span></li><li><em>releases:manage</em>: <span>The application will be able to edit and delete releases and release groups.</span></li><li><em>releases:read</em>: <span>The application will be able to read releases and release groups.</span></li><li><em>users:manage</em>: <span>The application will be able to edit (note creating) users</span></li><li><em>users:read</em>: <span>The application will be able to list users.</span></li><li><em>users_pii:read</em>: <span>The application will be able to read personally identifying information (names, email addresses, etc.) from (note-creating) users.</span></li><li><em>objectives:read</em>: <span>The application will be able to read objectives.</span></li><li><em>objectives:create</em>: <span>The application will be able to create new objectives.</span></li><li><em>objectives:manag</em>e: <span>The application will be able to edit and delete objectives.</span></li></ul></td></tr></tbody></table>

#### Proofpoint Protection Server

[Proofpoint Protection Server](https://www.proofpoint.com/us/resources/data-sheets/protection-server-technical-brief) is an email security gateway that protects organizations from spam, phishing, and malware threats by employing advanced filtering techniques and real-time threat detection to ensure secure email communication.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Username</li><li>Password</li><li>Server URL</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the administrator API role</td></tr></tbody></table>

#### Proofpoint TAP

[Proofpoint TAP](https://www.proofpoint.com/uk/products/advanced-threat-protection/targeted-attack-protection) (Targeted Attack Protection) is a cloud-based solution that detects, analyzes, and blocks advanced threats that target people through email, social media, and mobile apps.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Service principal</li><li>Secret</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Proofpoint TAP API</td></tr></tbody></table>

#### Proofpoint Threat Response

[Proofpoint Threat Response](https://www.proofpoint.com/us/products/advanced-threat-protection/threat-response) automates the investigation, prioritization, and remediation of security threats by integrating threat intelligence and orchestrating responses across security tools.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Proofpoint Threat Response API</td></tr></tbody></table>

#### Pulsedive

[Pulsedive](https://pulsedive.com/) is a cybersecurity platform that aggregates and analyzes threat intelligence data to identify potential security risks and provide organizations with insights to enhance their defense against cyber threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Pulsedive API</td></tr></tbody></table>

> **TIP:** Check out the [Pulsedive Authentication Guide](https://explained.tines.com/en/articles/8472721-pulsedive-authentication-guide) for more details on how to authenticate.

#### Rapid7 Insight

[Rapid7 Insight](https://www.rapid7.com/) is a cloud-based security analytics and automation platform that offers vulnerability management, incident detection, and response capabilities to help organizations improve their cybersecurity posture.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key (user key or organizational key)</li><li>Data region</li></ul></td></tr><tr><td>Scopes</td><td><ul><li>User key: inherits generating user's account permissions</li><li>Organization key: allows access to Insight product APIs and can only be generated by admins</li></ul></td></tr></tbody></table>

> **INFO:** This connect flow creates a credential for the following [products](https://docs.rapid7.com/insight/product-apis): InsightAppSec, InsightIDR, InsightOps, InsightConnect, Insight account controls, tCell, InsightVM V4 API

#### Recorded Future

[Recorded Future](https://www.recordedfuture.com/) provides a threat intelligence platform that analyzes vast amounts of data from across the internet to help organizations anticipate and mitigate cybersecurity risks.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API Key</td></tr><tr><td>Scopes</td><td>Defined by the user requesting the API key.</td></tr></tbody></table>



> **INFO:** Reach out to your Recorded Future account manager to request an API Key for use with Tines.

#### RegScale

​[RegScale](https://regscale.com/) is an AI-powered Continuous Controls Monitoring (CCM) platform that automates governance, risk, and compliance (GRC) processes—such as evidence collection, control assessments, and documentation—across 60+ regulatory frameworks, enabling organizations to achieve faster certifications, reduce audit preparation time, and integrate compliance into DevSecOps workflows. ​

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Token</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the RegScale API</td></tr></tbody></table>

#### Rippling

[Rippling](https://www.rippling.com/) is a cloud-based platform that integrates HR, IT, and finance systems to streamline employee management and business operations.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Rippling API</td></tr></tbody></table>

#### Rootly

[Rootly](https://rootly.com/) is an AI-native on-call and incident management platform that helps engineering teams detect, coordinate, resolve and learn from system incidents faster.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li></ul></td></tr><tr><td>Scopes</td><td>Access depends on the type of API key selected and the permissions available to the associated users Rootly profile.</td></tr></tbody></table>

#### SailPoint

[SailPoint](https://www.sailpoint.com/) is an enterprise-software company that helps organisations manage, secure and govern user and machine identities by giving them a unified platform to control access to data and applications.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:<br><ul><li>Client ID</li><li>Client Secret</li><li>Tenant URL</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the token</td></tr></tbody></table>

#### Salesforce

[Salesforce](https://www.salesforce.com/) is a cloud-based customer relationship management (CRM) platform that helps businesses manage their sales, marketing, customer service, and other related activities.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td><p>Environment you would like to connect to (Production or Sandbox).</p></td></tr><tr><td>Scopes</td><td><ul><li><em>api</em>: Manage user data via APIs. Allows access to the current, logged-in user's account using APIs, such as REST API and Bulk API 2.0. This scope also includes chatter_api, which allows access to Connect REST API resources.</li><li><em>id</em>: Access the identity URL service. Allows access to the identity URL service such as profile, email, address, and phone.</li><li><em>refresh_token</em>: <span>Allows a refresh token to be returned when the requesting client is eligible to receive one. With a refresh token, the app can interact with the user’s data while the user is offline.&nbsp;</span></li></ul></td></tr></tbody></table>

#### Secure Annex

[Secure Annex](https://secureannex.com/) is a browser extension security platform that provides a comprehensive analysis of the extensions installed in your organization.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Secure Annex API</td></tr></tbody></table>

#### Semgrep

[Semgrep](https://semgrep.dev/) is a static analysis tool that uses pattern matching to find bugs, security vulnerabilities, and enforce coding standards in source code across multiple programming languages.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Defined by the user generating the API token</td></tr></tbody></table>

#### SendGrid

[SendGrid](https://sendgrid.com/) is an email delivery platform that provides a scalable, API-first infrastructure for businesses to reliably send and track transactional and marketing emails.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Access depends on the type of API key selected and the permissions available to your SendGrid user profile</td></tr></tbody></table>

#### SentinelOne

[SentinelOne](https://www.sentinelone.com/) is a cybersecurity platform that provides endpoint protection, detection, and response by using AI-driven technology to prevent, detect, and remediate cyber threats in real-time.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>SentinelOne domain</li></ul></td></tr><tr><td>Scopes</td><td><span>The API token generated is limited to the RBAC role configuration defined for the token</span></td></tr></tbody></table>

> **TIP:** You may need to have your administrator Allow API Token Generation for your user. Details are in SentinelOne's documentation, which you can find under the Help dropdown on your navigation bar.

#### Sentry

[Sentry](https://sentry.io/) is an error monitoring and performance tracking platform that helps developers identify, triage, and resolve software issues in real-time across various programming languages and frameworks.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>Auth token</td></tr><tr><td>Scopes</td><td>Defined by the user generating the token. Scopes are listed <a href="https://docs.sentry.io/api/permissions/">here</a>.</td></tr></tbody></table>

#### ServiceNow

[ServiceNow](https://www.servicenow.com/) is a cloud-based platform that automates and manages IT service management (ITSM), enterprise workflows, and business operations across various departments.



The ServiceNow connect flow offers the below two options.

1. OAuth
2. Basic Authentication



> **IMPORTANT:** If you are connecting to an on-prem ServiceNow instance, you may need to whitelist your Tines tenant's IPs in ServiceNow. You can access your tenant's IP addresses by navigating to https://<your tenant ID>.tines.com/info



## Connect flow details

### Basic authentication

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Username</li><li>Password</li><li>ServiceNow domain</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the ServiceNow API that use basic authentication</td></tr></tbody></table>

### OAuth

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Determined by user creating the app.</td></tr></tbody></table>

> **TIP:** Check out the [ServiceNow Authentication Guide](https://explained.tines.com/en/articles/8637589-servicenow-authentication-guide) for more details on how to create your app.

#### Shodan

[Shodan](https://www.shodan.io/dashboard) is a search engine that scans and indexes internet-connected devices and systems worldwide, allowing users to discover and analyze various types of online assets, including servers, IoT devices, and industrial control systems.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Shodan API</td></tr></tbody></table>

> **TIP:** Check out the [Shodan Authentication Guide](https://explained.tines.com/en/articles/8630676-shodan-authentication-guide) for more details on how to authenticate.

#### Slack

[Slack](https://slack.com/) is a communication platform that facilitates real-time messaging, file sharing, and team collaboration, streamlining workflows and enhancing productivity within organizations.

## Connect flow details

The Slack connect flow offers the below two options. For self-hosted tenants, only custom app credentials are supported.

1. Use Tines's app for Slack (Managed OAuth type credential)
2. Use your own custom app (Text type credential)



> **TIP:** If you would like to use advanced functionality such as slack commands, modals, interactivity, and/or event subscriptions, we recommend creating your own custom app. You can implement base interactivity with Tines's app for Slack by using [pages](https://www.tines.com/docs/pages/) and [prompts](https://www.tines.com/docs/prompts/) in messages.



### Tines app for Slack

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Slack</td></tr><tr><td>Scopes</td><td><ul><li><em>channels:manage</em>: Manage public channels that Tines has been added to and create new ones</li><li><em>channels:read</em>: View basic information about public channels in a workspace</li><li><em>chat:write</em>: Send messages as @Tines</li><li><em>chat:write.public</em>: Send messages to channels @Tines isn't a member of</li><li><em>files:read</em>: View files shared in channels and conversations that Tines has been added to</li><li><em>files:write</em>: Upload, edit, and delete files as Tines</li><li><em>groups:write</em>: Manage private channels that Tines has been added to and create new ones</li><li><em>mpim:write</em>: Start group direct messages with people</li><li><em>reactions:read</em>: View emoji reactions and their associated content in channels and conversations that Tines has been added to</li><li><em>reactions:write</em>: Add and edit emoji reactions</li><li><em>team:read</em>: View the name, email domain, and icon for workspaces Tines is connected to</li><li><em>users.profile:read</em>: View profile details about people in a workspace</li><li><em>users:read</em>: View people in a workspace</li><li><em>users:read.email</em>: View email addresses of people in a workspace</li></ul></td></tr></tbody></table>



### Custom app for Slack

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>Slack OAuth Token for your custom app</td></tr><tr><td>Scopes</td><td>Defined by the user creating the custom Slack app.</td></tr></tbody></table>

> **TIP:** Check out the [Slack Authentication Guide](https://explained.tines.com/en/articles/8713184-slack-authentication-guide#h_d053e9ebc0) for more details on how to create your app.

#### SlashID

[SlashID](https://www.slashid.dev/) is an Identity Security company that offers a comprehensive view of identities, enabling informed decisions on identity hygiene, governance, attack detection, and response

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Organization ID</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the SlashID API</td></tr></tbody></table>

#### Snowflake

[Snowflake](https://www.snowflake.com/) is a cloud-based data warehousing platform that allows organizations to store, analyze, and share large volumes of data in a scalable and efficient manner.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td><p>In the first step of this connect flow, two SQL statements are provided for you to run in your Snowflake instance to create a security integration. They require an ACCOUNTADMIN role to run. After running the SQL statements, you should have the below items to provide in the next step:</p><ul><li>Client ID</li><li>Client secret</li><li>Snowflake URL</li><li>Access role for Tines to use to request access to your data</li></ul></td></tr><tr><td>Scopes</td><td><ul><li><em>session:role:&lt;custom-role&gt;</em>: Maps to a custom Snowflake role inputted by you in the previous step.</li><li><em>refresh_token</em>: Allows authorization server to use short lifetimes for access tokens without needing to involve the user when the token expires.</li></ul></td></tr></tbody></table>



> **IMPORTANT:** Snowflake does not allow the refresh token's validity to be increased beyond the 1 year maximum. See their [documentation](https://community.snowflake.com/s/article/FAQs-Snowflake-OAuth) for more information.

#### Snyk

[Snyk](https://snyk.io/) is a security platform that helps developers find, fix, and prevent vulnerabilities in their open source dependencies and containerized applications, enabling organizations to secure their software development lifecycle and mitigate security risks effectively.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Snyk</td></tr><tr><td>Scopes</td><td><ul><li><em>org.read</em>: View Organization information and settings</li><li><em>org.edit</em>: Edit Organization information and settings</li><li><em>org.report.read</em>: View reports in your Organization</li><li><em>org.project.create</em>: Add new Projects</li><li><em>org.project.read</em>: View Project information and settings and view Organization targets</li><li><em>org.project.edit</em>: Edit Project information</li><li><em>org.project.delete</em>: Permanently remove Projects and permanently remove Organization targets</li><li><em>org.project.status</em>: Activate and deactivate Projects</li><li><em>org.project.test</em>: Test Projects</li><li><em>org.project.ignore.create</em>: Create new Project ignores</li><li><em>org.project.ignore.read</em>: View Project ignore information</li><li><em>org.project.ignore.edit</em>: Configure Project ignores</li><li><em>org.project.ignore.delete</em>: Permanently remove Project ignores</li><li><em>org.project.tag.edit</em>: Create, apply and remove Project tags</li><li><em>org.project.pr.create</em>: Create fix pull requests for Projects</li><li><em>org.project.pr.skip</em>: Skip failed security tests on pull requests by marking checks as successful</li><li><em>org.project.jira.issue.read</em>: View Jira issue information</li><li><em>org.project.jira.issue.create</em>: Create new Jira issues</li><li><em>org.package.test</em>: Test packages in ecosystems supported by Snyk</li></ul></td></tr></tbody></table>

#### SolarWinds Observability Platform

[SolarWinds Observability platform](https://www.solarwinds.com/) provides unified monitoring and analytics across infrastructure, applications, and digital experiences to help organizations quickly identify and resolve IT performance issues.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Username</li><li>Password</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user account</td></tr></tbody></table>

#### SolarWinds Service Desk

[SolarWinds](https://www.solarwinds.com/service-desk) Service Desk is a cloud-based, AI-powered IT service management  platform that streamlines incident, request, asset, change, and configuration management with automation and user-friendly tools.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Basic Auth</td></tr><tr><td>Required inputs</td><td><p>Username, Password and SolarWinds ITSM Domain</p></td></tr><tr><td>Scopes</td><td>Allows access to all API endpoints that the user account has permission to.</td></tr></tbody></table>

#### Splunk Enterprise

[Splunk Enterprise](https://www.splunk.com/en_us/products/splunk-enterprise.html) is a data analytics platform that collects, indexes, and analyzes machine-generated data to provide operational intelligence, monitoring, and security insights across an organization's IT infrastructure.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><p>Access token:</p><ul><li>Access token</li><li>Base URL</li></ul><p>Basic auth:</p><ul><li>Username</li><li>Password</li><li>Base URL</li></ul></td></tr><tr><td>Scopes</td><td>Access is determined by the user and authorization type selected.</td></tr></tbody></table>

> **TIP:** Check out the [Splunk Enterprise Authentication Guide](https://explained.tines.com/en/articles/8851605-abnormal-security-authentication-guide) for more details on how to authenticate.

#### Splunk SOAR

[Splunk SOAR](https://www.splunk.com/en_us/products/splunk-security-orchestration-and-automation.html) (Security Orchestration, Automation, and Response) is a platform that helps security teams automate and orchestrate complex security workflows, enabling faster incident response, threat detection, and remediation across various security tools and systems.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><p>JSON object with the following:</p><ul><li><span data-pm-slice="1 1 []">ph-auth-token</span></li><li><span data-pm-slice="1 1 []">server</span></li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user generating the token.</td></tr></tbody></table>

#### Spotify

[Spotify](https://spotify.com/) is a digital music, podcast, and audiobook streaming service that gives users access to millions of tracks and personalized listening experiences.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:<br><ul><li>Application (client) ID</li><li>Client secret value</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td><p>Allows access to Spotify's Web API</p></td></tr></tbody></table>

#### Spur

[Spur](https://spur.us/) provides cutting-edge tools and data to detect VPNs, residential proxies, and bots – helping you stay ahead of the latest evasion methods.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Spur API</td></tr></tbody></table>

#### Stairwell

[Stairwell](https://stairwell.com/) is a cybersecurity intelligence platform that maintains a private, searchable archive of an organization’s executable files to provide continuous, retroactive threat detection and malware analysis.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Token</li></ul></td></tr><tr><td>Scopes</td><td><p>Access depends on the permissions available to your Stairwell user profile.</p></td></tr></tbody></table>

#### Strava

[Strava](https://www.strava.com/) is a social fitness platform that allows users to track and share their physical activities like running, cycling, and swimming through GPS data, while connecting with a community of athletes.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the app.</td></tr></tbody></table>

#### Stripe

[Stripe](https://stripe.com/) provides online payment processing and economic infrastructure for businessess to accept and manage payments, billing, fraud prevention and other financial services.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li></ul></td></tr><tr><td>Scopes</td><td>Access depends on the permissions available to the associated users Stripe profile.</td></tr></tbody></table>

#### Sublime Security

[Sublime Security](https://sublime.security/) provides an open-source email security platform that automates the detection and response to phishing and other email-based threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Sublime Security API</td></tr></tbody></table>

> **TIP:** Check out the [Sublime Security Authentication Guide](https://explained.tines.com/en/articles/8504873-sublime-security-authentication-guide) for more details on how to authenticate.

#### Sumo Logic

[Sumo Logic](https://www.sumologic.com/) is a modern, cloud-native log analytics and continuous intelligence platform that centralizes and analyzes machine-generated data. Empowering organizations with real-time observability, security insights (like Cloud SIEM), and AI-powered automation for monitoring, troubleshooting, and threat detection

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Access ID</li><li>Access Key</li><li>Site</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints that the account running the key has access to.</td></tr></tbody></table>

> **TIP:** Check out the [Sumo Logic Authentication Guide](https://explained.tines.com/en/articles/8765832-sumo-logic-authentication-guide) for more details on how to authenticate.

#### Synack

[Synack](https://www.synack.com/) provides continuous crowdsourced security testing through ethical hackers and AI-powered technology to identify and remediate vulnerabilities in clients' systems.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li></ul><p>When generating this API token, you must input whitelisted IPs. You can find your Tines tenant IPs at `https://your-tines-tenant.tines.com/info`</p></td></tr><tr><td>Scopes</td><td>The API token is generated for a user and limited to the permissions associated with that user.</td></tr></tbody></table>

#### Sysdig

[Sysdig](https://sysdig.com/) is a cloud and container security platform that provides visibility, security, and forensics for containers, Kubernetes, and cloud environments through monitoring, threat detection, and compliance capabilities.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API token</li><li>Your Sysdig tenant region</li></ul></td></tr><tr><td>Scopes</td><td>Specific to the user and team the token is created within.</td></tr></tbody></table>

> **TIP:** Check out the [Sysdig Authentication Guide](https://explained.tines.com/en/articles/8637422-sysdig-authentication-guide) for more details on how to authenticate.

#### Tavily

[Tavily](https://tavily.com/) is an information retrieval tool that uses AI to search, verify, and summarize data from multiple online sources, streamlining research and fact-finding processes.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the Tavily API</td></tr></tbody></table>

#### Tenable Security Center

[Tenable Security Center](https://www.tenable.com/products/security-center) is an on-prem cybersecurity platform that provides comprehensive vulnerability management and threat detection capabilities for organizations to assess, prioritize, and remediate security risks across their IT infrastructure.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Access Key</li><li>Secret Key</li><li>Tenable Security Center domain</li></ul></td></tr><tr><td>Scopes</td><td>The API key is generated for a user and limited to the permissions set on the role associated with that user. Ensure the user has the correct role assigned.</td></tr></tbody></table>

#### Tenable Vulnerability Management

[Tenable Vulnerability Management](https://www.tenable.com/products/vulnerability-management) is a comprehensive solution for identifying, assessing, prioritizing, and remediating vulnerabilities across an organization's IT infrastructure to reduce cyber risk.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Access Key</li><li>Secret Key</li></ul></td></tr><tr><td>Scopes</td><td>The API key is generated for a user and limited to the permissions set on the role associated with that user. Ensure the user has the correct role assigned.</td></tr></tbody></table>

#### TheHive Project

[TheHive Project](https://thehive-project.org/) is an open-source security incident response platform designed to help security teams manage and analyze security incidents efficiently, collaborate effectively, and automate response actions to mitigate cybersecurity threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>TheHive Project URL of your instance</li></ul></td></tr><tr><td>Scopes</td><td>The API key generated is limited to the permissions set on the user profile that generated the key. Customers often create an account for Tines to associate the API key with, scoping the permissions for that user as desired.</td></tr></tbody></table>

> **TIP:** Check out the [TheHive Project Authentication Guide](https://explained.tines.com/en/articles/7967137-thehive-authentication-guide) for more details on how to authenticate.

#### ThreatConnect

[ThreatConnect](https://threatconnect.com/) is a threat intelligence platform that helps organizations aggregate, analyze, and act on cyber threat information to improve their security posture and decision-making.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Access ID</li><li>Secret key</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the system role assigned for the API user</td></tr></tbody></table>

#### Tines

[Tines](https://www.tines.com/) is a** **platform purpose-built to automate and integrate business processes for the whole team.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>Type of API key to generate: tenant owner, team, or personal</li><li>Name of API key credential to generate</li><li>If a team API key is selected, the team to generate the key for</li></ul></td></tr><tr><td>Scopes</td><td><p>This varies based on the type of key selected:</p><ul><li>Tenant owner: access to the entire tenant</li><li>Team: role-based access to a specific team on the tenant</li><li>Personal: a key tied to your identity and permissions</li></ul></td></tr></tbody></table>

#### Traceable AI

[Traceable AI](https://traceable.ai/) secures APIs by automatically discovering, monitoring, and protecting them from threats using real-time analytics and machine learning, while ensuring compliance and safeguarding sensitive data.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to <span>Traceable's public API</span></td></tr></tbody></table>

#### Trello

[Trello](https://trello.com/) is a visual project management tool that helps teams organize tasks and workflows using boards, lists, and cards in a flexible, collaborative interface.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Token</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user generating the token</td></tr></tbody></table>

> **TIP:** Check out the [Trello Authentication Guide](https://explained.tines.com/en/articles/8472761-trello-authentication-guide) for more details on how to authenticate.

#### Twilio

[Twilio](https://www.twilio.com/) is a cloud communications platform that empowers businesses to integrate voice, messaging, and video capabilities into their applications and services. By offering programmable APIs and scalable infrastructure, Twilio enables developers to build custom communication solutions that enhance customer engagement, streamline operations, and drive innovation across various industries.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API Key</li><li>Secret</li><li>Account SID</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the API key.</td></tr></tbody></table>

#### Typeform

[Typeform](https://www.typeform.com/) is an online survey and form-building tool that allows users to create interactive and engaging surveys, questionnaires, quizzes, and forms with a conversational interface, enabling businesses to collect data, gather feedback, and engage with their audience in a visually appealing and user-friendly way.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Typeform</td></tr><tr><td>Scopes</td><td><ul><li><em>forms:read</em>: <span>Retrieve existing forms in your Typeform account. Retrieve customizable messages for your forms</span></li><li><em>forms:write</em>: <span>Create, update, and delete your forms</span></li><li><em>webhooks:read</em>: Retrieve data about specified Webhooks in your Typeform account</li><li>webhooks:write: Create, update, and delete specified Webhooks in your Typeform account</li><li><em>responses:read</em>: Retrieve form responses, landing, and submission information</li><li><em>accounts:read</em>: <span>Retrieve your name, email, and account language</span></li><li><em>workspaces:read</em>: Request data about your workspaces</li><li><em>offline</em>: Request data on your behalf when you're not online</li></ul></td></tr></tbody></table>

#### UnpacMe

[UnpacMe](https://www.unpac.me/) is a cloud-based automated malware analysis and unpacking service that helps security professionals analyze suspicious files and malware samples by detonating them in a secure sandbox environment and providing detailed analysis reports.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the UnpacMe API</td></tr></tbody></table>

#### UpGuard

[UpGuard](https://www.upguard.com/) is a comprehensive cybersecurity platform specializing in third-party risk and attack surface management. It offers tools for continuous monitoring, security ratings, vendor risk assessments, and data leak detection, enabling organizations to identify, assess, and mitigate cyber risks associated with their own systems and third-party vendors.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the UpGuard API depending on chosen scopes in UI.</td></tr></tbody></table>

#### Uptycs

[Uptycs](https://www.uptycs.com/) is a security analytics platform that uses osquery to provide real-time monitoring, intrusion detection, and compliance across cloud workloads, containers, and endpoints.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>JWT</td></tr><tr><td>Required inputs</td><td>Downloaded JSON file provided by Uptycs. This will include the Customer ID, API key, Secret, and API domain.</td></tr><tr><td>Scopes</td><td><span>Defined by the permissions set on the user profile the key is generated within.</span></td></tr></tbody></table>

#### Upwind

[Upwind](https://www.upwind.io/) is a cloud-security platform offering runtime-based visibility and protection across cloud infrastructure, applications and supply chains.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:<br><ul><li>Client ID</li><li>Client Secret</li><li>Org ID</li><li>Audience</li></ul></td></tr><tr><td>Scopes</td><td><p>Allows access to all endpoints in the Upwind API</p></td></tr></tbody></table>

#### URLScan.io

[URLScan.io](https://urlscan.io/) is a service that scans and analyzes URLs for potential security threats, such as phishing scams, malware, or malicious content.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the URLScan.io API</td></tr></tbody></table>

> **TIP:** Check out the [URLScan.io Authentication Guide](https://explained.tines.com/en/articles/7949649-urlscan-io-authentication-guide) for more details on how to authenticate.

#### Vanta

[Vanta](https://www.vanta.com/) is an automated security and compliance platform that helps companies achieve and maintain compliance standards like SOC 2, ISO 27001, and HIPAA.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client secret</li><li>Scopes</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the user creating the client ID and secret</td></tr></tbody></table>

#### VirusTotal

[VirusTotal](https://www.virustotal.com/gui/home/upload) is a web-based service that scans files and URLs using multiple antivirus engines and security tools to detect and analyze potential malware and other security threats.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API key</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the VirusTotal API</td></tr></tbody></table>

> **TIP:** Checkout the [VirusTotal Authentication Guide](https://explained.tines.com/en/articles/7853769-virustotal-authentication-guide) for more details on how to authenticate.

#### VMRay

[VMRay](https://www.vmray.com/) is a malware analysis platform that uses dynamic analysis to detect and analyze advanced threats by executing suspicious files in a controlled environment and monitoring their behavior.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td><ul><li>API key</li><li>Domain</li></ul></td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the VMRay Security API</td></tr></tbody></table>

#### WiGLE

[WiGLE](https://wigle.net/) (Wireless Geographic Logging Engine) is a platform that collects and maps information about wireless networks worldwide, allowing users to search and contribute data on Wi-Fi access points and their locations.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Text</td></tr><tr><td>Required inputs</td><td>API token</td></tr><tr><td>Scopes</td><td>Allows access to all endpoints in the WiGLE API</td></tr></tbody></table>

#### Wiz

[Wiz](https://www.wiz.io/) is a cybersecurity company that specializes in providing cloud security solutions, including vulnerability detection, configuration monitoring, and threat detection, to help businesses protect their cloud infrastructure and applications from security threats and breaches.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><ul><li>Client ID</li><li>Client Secret</li><li>Authorization token URL</li><li>GraphQL endpoint URL</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user creating the service account.</td></tr></tbody></table>



> **TIP:** Check out the [Wiz Authentication Guide](https://explained.tines.com/en/articles/8623326-wiz-integration-guide#h_5a703edbbd) for more details on how to authenticate.

#### Zendesk

[Zendesk](https://www.zendesk.com/) is a customer service software platform that helps businesses manage customer support tickets, inquiries, and interactions across various channels, streamlining customer service operations and enhancing customer satisfaction.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>Managed OAuth</td></tr><tr><td>Required inputs</td><td>N/A: sign in with the account you'd like the credential associated with in Zendesk</td></tr><tr><td>Scopes</td><td><ul><li><em>read</em>: gives access to GET endpoints. Includes permission to sideload related resources</li><li><em>write</em>: gives access to POST, PUT, and DELETE endpoints</li></ul></td></tr></tbody></table>

#### Zoom

[Zoom](https://www.zoom.com/) is a cloud-based video communications platform that enables virtual meetings, webinars, conference calls, chat, and collaboration through audio, video, and screen-sharing features.

## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>HTTP Request</td></tr><tr><td>Required inputs</td><td><p><strong>Activated</strong> Server to Server OAuth App</p><ul><li>Account ID</li><li>Client ID</li><li>Client secret</li></ul></td></tr><tr><td>Scopes</td><td>Defined by the user creating the app.</td></tr></tbody></table>



> **TIP:** Please note the connect flow currently only supports Server to Server OAuth apps. Check out the [Zoom Authentication Guide](https://explained.tines.com/en/articles/8286340-zoom-authentication-guide) for more details on how to authenticate.

#### Zuora

[Zuora](https://www.zuora.com/) is a cloud-based subscription-management and monetization platform that helps companies launch, manage and scale recurring-revenue business models by automating billing, payments, quoting, revenue recognition and subscription lifecycle management.


## Connect flow details

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Item</td><td>Description</td></tr><tr><td>Credential type</td><td>OAuth 2.0</td></tr><tr><td>Required inputs</td><td>At various steps you will be prompted to enter the following:<br><ul><li>REST root URL</li><li>Client ID</li><li>Client Secret</li></ul></td></tr><tr><td>Scopes</td><td>Determined by the permissions of the associated user</td></tr></tbody></table>

## Events

## Introduction

Events represent the structured data sent between actions. Events are JSON objects making them flexible and easy to read. All events are timestamped and "write-once", providing an audit trail and making them an ideal basis for calculation of key performance metrics.

> **TIP:**
> To learn more about how to reference event data, see our [Formulas docs](https://www.tines.com/docs/formulas/).
> 
> To learn more about how to loop through event data, see [Looping](https://www.tines.com/docs/actions/types/event-transformation/message-only/#looping).

## Event Structure

When an event is emitted by a Tines action, it will contain the result of the action and, if it exists, the received event. For example, the below image shows an event emitted by an action called "Type is infection". The "Type is infection" action is configured to receive events from an action called "Receive events":

![](https://www.datocms-assets.com/55802/1698947994-event_structure_image.png)

> **IMPORTANT:** The result of each action is contained in an object whose key is an underscored, lowercase form of the action's name (i.e., [snake cased](https://en.wikipedia.org/wiki/Snake_case)). For example: if the action's name is 'Send email', the result of the action will be contained in a key titled 'send\_email' in the emitted event.

## Re‑emit Events

It's possible to duplicate an existing event and pass it down to all receiving actions by selecting the event and clicking the **Re‑emit** button from the Action Events list. This can be used to repeatedly test the behaviour of a receiving action with the same data.

**Note**: There is a limit of 500 events that can be re-emitted at one time. 

![](https://www.datocms-assets.com/55802/1718626767-screenshot-2024-06-17-at-13-17-58.png)

## Daily Event Limits

> **NOTE:** Daily event limits **do not** apply to our [Community Edition](https://www.tines.com/get-started-with-tines-community-edition/) or [Starter Edition](https://www.tines.com/pricing/), both of which use monthly event limits. For more information, check out our edition comparison [here](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging#h_4dce8c5280).

While Tines offers extremely generous event limits on all paid plans, there is a daily total event limit at the story and tenant level. When a story or tenant has reached 80 percent of its event limit, the owner of the story and the admins of the tenant will be notified via email. Tenant owners can also configure notifications to be sent at additional event usage percentages and to additional users and webhooks. If a story or tenant does reach its daily event limit, runs for the relevant story or tenant will be paused until the following day when the limit resets. 

This is extremely rare and most frequently occurs when there is an infinite loop executing. You can always reach out to a member of our team for help if you encounter this situation.

If you have version control enabled for your tenant, the story event limit is separate for the live and test version of the story.

Tenant owners can also configure daily event limits for individual teams. Read more about this in the [event limit settings](https://www.tines.com/docs/event-limit-alerts/) documentation.

## Formulas

Just like in spreadsheets, formulas in Tines allow you to transform data. Where a cell in a spreasheet uses a formula to refer to other cells, a formula in a Tines action references upstream actions or other data sources throughout the platform.

Here are a few examples of what you can do with formulas:

```python
# convert some text to uppercase
UPCASE(user.job)

# logic
IF(user.job = "Engineer", "likes code", "dislikes code")

# tap into our large library of powerful functions
CSV_PARSE(fetch_csv.body)
```

## Adding formulas

Formulas are most frequently used in actions. Add a formula expression to a text field inside a 'pill' using the inserter in action options:

![](https://www.datocms-assets.com/55802/1655989491-formulas_insert-071d8df1c46ed579cb39ec4538592d76.gif)

## Editing formulas

Use the popup formula builder to author your formula expression. We’ll show in-line help and documentation as you type, and a preview of the result. 

![](https://www.datocms-assets.com/55802/1655989503-formulas_edit-8c15dee6f7ad6f0e4d3e8ff94e4c8111.png)

## Formulas pills inside text fields: tags vs values

There are two types of pill: values and tags.

Value pills are replaced with the result of their formula expression when the action runs.

Tags on the other hand control the flow of execution. Tags are particularly useful for doing things like building emails

![](https://www.datocms-assets.com/55802/1655989516-formulas_tags_example-85a6f213876973c12a94d3d9325b5f2d.png)

## Formula pills in text fields, vs formula fields

When you use a  pill within a piece of text, the value is converted to text and joined with any surrounding text. This isn't always what you want, sometimes you will want to add a complex object directly to a payload.

To do this you can use a formula field, instead of a text field:

![](https://www.datocms-assets.com/55802/1660731003-single_value_mode_explode_users.gif)

Using a formula field will return the value directly, rather than converting it to text. This is useful for cases where you want to return a dynamic non-text value, like an array or an object.

## Slugs

Slugs are how we handle all references to specific objects in Tines: actions, resources, credentials, inputs and stories are all referenced via slugs. A slug is what you get when you "sluggify" an object's name, which involves a few steps:

1. Converting all letters to lowercase.
2. Replacing any series of special symbols (punctuation, whitespace, any character other than letters and numbers) that appears in the middle of a name with a single underscore. Special characters at the start or end of a name are deleted.
3. If the name starts with a number, prepend it with an underscore

This is done to ensure that all names can be referenced through a combination of lowercase letters, numbers, and underscores. Here are some examples of sluggifications:

- `My action` => `my_action`
- `Lots        of         spaces` => `lots_of_spaces`
- `Series!!!!of!!!!special!!!!characters` => `series_of_special_characters`
- `Japanese 日本語の文字 characters` => `japanese_characters`
- `!!!Starts and ends with special characters!!!` => `starts_and_ends_with_special_characters`
- `Numbers in middle 100 or end 200` => `numbers_in_middle_100_or_end_200`
- `1 starts with a number` => `_1_starts_with_a_number`

Occasionally, slugs will collide. If an upstream and downstream action have the same slug, the downstream action will get the reference. You cannot have a pair of resources or a pair of credentials on the same team with the same slug, as an error will be raised when you try to do so. However, it is possible to have a pair of matching resource or credential slugs across two separate teams.

##### Multipart slugs

Sometimes you may wish to reference the subfield of an object. This can be done using the following:

```json
{
  "event":
  {
    "body": "foobar"
  }
}
```

- `event.body`

`If the keys contain spaces the following can be used:`

```
{
  "event":
  {
    "body with spaces": "foobar"
  }
}
```

- `event["body with spaces"]`

When passing in multipart slugs with spaces into a formulas function, use the following syntax:

```
{
  "array": 
    [
      {
        "Jira Team Name": "Accommodations Management",
      },
      {
        "Jira Team Name": "Travel Management",
      },
    ]
}
```

- `WHERE(array, "[\"Jira Team Name\"]", "Accommodations Management")`

or

- `WHERE(array, "['Jira Team Name']", "Accommodations Management")`

### Examples

### Access data from an upstream action’s event

```
upstream_action.path.to.value
```

See more information on referencing data.

### Count the number of items in an array

```python
SIZE(my_array)
```

See the full list of [functions](https://www.tines.com/docs/functions/).

### Multiply two numbers

```python
my_number * 10
```

See the full list of [operators](https://www.tines.com/docs/operators/).

### Generate a random number between 1 and 10

```python
RANDOM(1, 10)
```

### Get the current date and time in yyyymmdd Format

```python
DATE("now", "%Y%m%d")
```

### Get the time 24 hours ago

```python
DATE(DATE("now", "%s") - 24 * 60 * 60, "%Y-%m-%dT%l:%M:%S%z")
```

### Select all the elements in an array where the key has the given value

Input:

```python
[
  {
    "color": "red",
    "index": "1"
  },
  {
    "color": "blue",
    "index": "2"
  },
  {
    "color": "green",
    "index": "3"
  }
]
```

```python
WHERE(my_array, "index", "1")
```

Output:

```python
[{"color":"red","index":"1"}]
```

### Select all the elements in an array that regex match the string "r"

Input:

```python
[
  "red",
  "blue",
  "green"
]
```

```python

FILTER(my_array, LAMBDA(element, MATCH(element, "r")))
```

Output:

```python
["red","green"]
```

### Get the difference between two arrays (i.e. get elements from one that are not present in the other)

Input:

```python
array_one = [
  "dog",
  "cat",
  "turtle",
  "dinosaur",
  "lizard",
  "chicken",
  "koala"
]
array_two = [
  "cat",
  "elephant",
  "giraffe",
  "penguin",
  "tiger",
  "koala"
]
```

```python
FILTER(array_two, LAMBDA(arr_two_elem, NOT(INCLUDES(array_one, arr_two_elem))))
```

Output:

```python
[
  "elephant",
  "giraffe",
  "penguin",
  "tiger"
]
```

### Construct an array of objects from an input array

Input:

```python
[
  "foo@tines.com", 
  "bar@tines.com"
]
```

```python
MAP_LAMBDA(email_array, LAMBDA(elem, OBJECT(\"email_address\", OBJECT(\"address\", elem))))
```

Output:

```python
[
  {
    "email_address": {
      "address": "foo@tines.io"
    }
  },
  {
    "email_address": {
      "address": "bar@tines.io"
    }
  }
]
```

### Referencing data

Formulas are all about referencing and transforming data. References can be derived from three locations:

1. Event data from an upstream action
2. Content of Credentials, Pages, Resources
3. Information about your environment 

## Reference data from upstream actions

Say we’re evaluating a formula's expression in **Action B**, which is downstream from **Action A**. Perhaps **Action A** had fetched a list of users from a remote API, i.e. Action A has produced an [event](/docs/events/#event-structure).

Now, **Action B** can make a reference to that data inside the event — for instance to count it:

```
COUNT(action_a.body.collections.users)
```

Simply refer to the [snake cased](https://en.wikipedia.org/wiki/Snake_case) name of the upstream action to access its data. Learn more [here](https://www.tines.com/docs/actions/configuration/config-events-and-values/). 

### Examples

**Receive email to HTTP Request** 

A "**Receive Email Action**" is connected to a downstream "HTTP Request Action". When an email is received, it produces an event which may look like:

```
{
  "receive_email_action": {
    "from": "potential_phishing@example.com",
    "subject": "Free gift cards",
  }
}
```

You can then put the sender's email in the downstream** **"HTTP Request Action" to check the sender domain:

```
# Access "Receive Email Action"'s sender email
receive_email_action.from

# It will produce
"potential_phishing@example.com"
```

**Page to record**

A page called "**Submit feedback**" is connected to a downstream "Capture record" tool. When the page is submitted, it produces an event containing the user's email and their feedback. The [page event](/docs/pages/collecting-input-with-forms/) may look like:

```
{
  "submit_feedback": {
    "body": {
      "feedback": "Sample feedback",
      "email": "user@example.com",
    }
}
```

You can then create a record by referencing the page's feedback in the "Capture record" tool:

```
# Access "Submit feedback"'s feedback
submit_feedback.body.feedback

# It will produce
"Sample feedback"
```



## Access data from credentials, pages, resources

Data stored in these three areas can be dynamically referenced at any time using the following keywords:

### From credentials

The `CREDENTIALS` key works identically to the previous example, for fetching [credential](/docs/credentials/) tokens. (Note: because these are inherently sensitive, we’ll never output a preview value in the formula builder when working with credentials.)

```
CREDENTIAL.example_api_key
```

If you want to dynamically reference credentials based on a variable, there are a couple of ways to do so:

```
# Upstream action payload
{
  credential_name: "example_api_key",
}

# Downstream functions
CREDENTIAL[upstream_action.credential_name]
GET(CREDENTIAL, upstream_action.credential_name)
```

### From pages

Use the special `PAGE` key to access all [Pages](/docs/pages) in the current [Story](/docs/story). If your current Story has a page tool named "**Get Email Updates**", then `PAGE.get_email_updates` will retrieve the [page's URL](/docs/pages/distribution-and-access-control/#customizing-the-url). 

```
# To access the "Get Email Updates" page in anywhere in your story:
PAGE.get_email_updates

# It will return the URL of that page:
"https://your-tenant.tines.com/pages/your_page_identifier"
```

### From resources

Use the special `RESOURCE` key to access data residing in [resources](/docs/resources). Let’s say we have a text resource **"Domain name"**. The following expressions would fetch it:

```
RESOURCE.domain_name
```

If you want to dynamically reference resources based on a variable, there are a couple of ways to do so:

```
# Upstream action payload
{
  resource_name: "domain_name",
}

# Downstream functions
RESOURCE[upstream_action.resource_name]
GET(RESOURCE, upstream_action.resource_name)
```



## Access information about your environment

Use the info, metadata, and paginate

### Info

The `INFO` key exposes relevant information about cases, credentials, resources, and records. 

**Credentials**: Access a credential's name, ID, and metadata values.

```
INFO.credential.example_credential_name.metadata.example_metadata_key
```

**Resources**: Access a resource's name and ID.

```
INFO.resource.example_resource_name.id
```

**Records**: Access a record's ID, name, and fields. For each field, access its ID and name.

```
INFO.record.example_record_name.fields.story_name.id
```

**Cases**: Access case sub-statuses and case fields within the team. 

- For each sub-status, view its ID and name.
- For each case field, view the ID, name, slug, markdown reference, type,  and validation options

```
INFO.cases.statuses.sub_status_name.id
INFO.cases.case_field_input_name.id
```

### Metadata

The `META` key exposes a useful set of information about the current environment. Here’s the full data structure it generates:

```json
"tenant": {
  "domain": "example.tines.com",
  "name": "example",
  "url": "https://example.tines.com"
},
"team": {
  "id": 123,
  "name": "Example team",
  "groups": {
    "case_group1": {
      "id": 12345,
      "name": "case group1"
    }
}
},
"story": {
  "id": 123,
  "name": "Example story",
  "key": "example_story",
  "is_draft": false,
  "draft": {
    "id": 12345,
    "name": "draft name"
  }
},
"action": {
  "id": 123,
  "name": "Example action",
  "key": "example_action"
},
"event": {
  "id": 123456789,
  "payload": {
    "array": [
      "the",
      "current",
      "payload",
      "being",
      "processed"
    ]
  }
}
"story_run": {
  "id": "fc7b6392-b602-465b-8f58-ee07d33fa80e"
}
```

So, for example, to access the name of the current story, you could use:

```
META.story.name
```

### Paginate

The `PAGINATE` key can be used in HTTP Request Actions with pagination enabled to access information about the pagination run.

**Index**: Returns the current index of the pagination loop, starting at 0 for the first page request.

```
PAGINATE.index
```

**Previous response**: Returns the HTTP response of the previous page. This is useful when you need to get a link to the next page or determine the total number of pages left. This value will be `null` for the first request.

```
PAGINATE.previous_response
```

Learn more about using `PAGINATE.index` and `PAGINATE.previous_response` in our [HTTP Request Action pagination guide](/docs/actions/types/http-request/#pagination).



## All formula keywords

Below is a list of all keywords you can reference when building a formula.

| Keyword | Description |  |
| --- | --- | --- |
| `CREDENTIAL` | Reference sensitive information without exposing its value |  |
| `FORM` | (Deprecated) Please use `PAGE` |  |
| `INFO` | Access metadata for [cases](https://www.tines.com/docs/cases/), [credentials](https://www.tines.com/docs/credentials/), [resources](https://www.tines.com/docs/resources/), and [records](https://www.tines.com/docs/records/) |  |
| `INPUT` | Access [action inputs](https://www.tines.com/docs/actions/templates/private-templates/#action-inputs) |  |
| `LOCAL` | Access data from the [local values](https://www.tines.com/docs/actions/configuration/#common-options) object |  |
| `LOOP` | Access data about the current [loop iteration](https://www.tines.com/docs/stories/groups/#looping). For each iteration of the loop, a `LOOP` object will be provided will contain: `value`, `index`, `key` and `previous_result` |  |
| `META` | Access data about the current environment including [looping page elements;](https://www.tines.com/docs/pages/looping-page-elements/) see above for full data structure |  |
| `OUTPUT` | Access the output of the action to modify the event data that it produces; see details on usage [here](https://www.tines.com/docs/actions/configuration/#common-options) |  |
| `PAGE` | Access the URL of a [page object](https://www.tines.com/docs/pages/) |  |
| `PAGINATE` | Used in HTTP Request Actions with [pagination](https://www.tines.com/docs/actions/types/http-request/#pagination) enabled to access information about the pagination run |  |
| `RESOURCE` | Access data from [Resources](https://www.tines.com/docs/resources/), which store information in a single, centralized location for your team |  |
| `STORY` | Used to get ID for [Send to Story](https://www.tines.com/docs/stories/send-to-story/) actions |  |

### Functions

#### ABS

```
ABS(number)
```

Returns the absolute value of a number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": -17
  }
}
```

Formula:

```
ABS(my_action.message)
```

Output:

```json
17
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 4
  }
}
```

Formula:

```
ABS(my_action.message)
```

Output:

```json
4
```

### Example 3: ABS will also work on text that only contains a number.

Input:

```json
{
  "my_action": {
    "message": "-19.86"
  }
}
```

Formula:

```
ABS(my_action.message)
```

Output:

```json
19.86
```

## Sample actions

```json
{
  "standardLibVersion": "90",
  "actionRuntimeVersion": "73",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "-35"
        }
      },
      "position": {
        "x": 150,
        "y": 540
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "aiMonitoringThresholds": [],
      "caseConfiguration": {
        "subStatus": null,
        "tags": []
      },
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "templateTags": [],
      "originStoryIdentifier": "cloud:00e45749508fe15ca1af3397eab8db78:18f32aa494b49ac605dcc3aa0eda65cc"
    },
    {
      "disabled": false,
      "name": "ABS",
      "description": "Returns the absolute value of a number",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "abs": "=ABS(my_action.example)"
        }
      },
      "position": {
        "x": 150,
        "y": 645
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "aiMonitoringThresholds": [],
      "caseConfiguration": {
        "subStatus": null,
        "tags": []
      },
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "templateTags": [],
      "originStoryIdentifier": "cloud:00e45749508fe15ca1af3397eab8db78:18f32aa494b49ac605dcc3aa0eda65cc"
    }
  ],
  "sections": [],
  "links": [
    {
      "sourceIdentifier": "0",
      "receiverIdentifier": "1"
    }
  ],
  "diagramNotes": []
}
```

#### ACOS

```
ACOS(x)
```

Computes the arc cosine of x (in radians), returning a number between 0 and π

**Categories:** Numbers

#### AES_DECRYPT

```
AES_DECRYPT(encoded_text, key, [iv])
```

Note: AES_ENCRYPT and AES_DECRYPT are now deprecated. Please use either TINES_ENCRYPT/TINES_DECRYPT or OPENSSL_ENCRYPT/OPENSSL_DECRYPT instead.

Decrypts text using AES

The input must be AES-256-CBC Base64 encoded or be the direct output of AES_ENCRYPT. An initialization vector can optionally be passed as a third argument.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
AES_DECRYPT(my_action.encrypted_message, CREDENTIAL.secret_key)
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "otZMcEgEhODINSIB1g3Yz9lqUBZymCp7QiP0ajBeXFAWRfZerTYkDF5jormeYSBr"
        }
      },
      "position": {
        "x": 1980,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AES_DECRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_decrypt": "=AES_DECRYPT(my_action.message, CREDENTIAL.aes_secret_key, CREDENTIAL.aes_iv)"
        }
      },
      "position": {
        "x": 1980,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "AES_DECRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_decrypt": "=AES_DECRYPT(my_action.message, CREDENTIAL.aes_secret_key)"
        }
      },
      "position": {
        "x": 2160,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "kexGSJpw8MUpqJ8BZsZau3sCeX6t29GzrJNRIAiM7oTmqw9KRUxaPT/qCufj5hpE"
        }
      },
      "position": {
        "x": 2160,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### AES_ENCRYPT

```
AES_ENCRYPT(text, key, [iv], [mode: "aead"], [expires_in])
```

Note: AES_ENCRYPT and AES_DECRYPT are now deprecated. Please use either TINES_ENCRYPT/TINES_DECRYPT or OPENSSL_ENCRYPT/OPENSSL_DECRYPT instead.

Encrypts text using AES

The output is encoded with character set "[A-Za-z0-9+/=\.]" (base64 + '.').

`AES_ENCRYPT(text, key, [iv], [mode: "aead"], [expires_in])`

text: The plaintext to be encrypted.

key: A 32 byte secret value to be used for encryption.

iv: A 16 byte value that must never be repeated. Note that when the "mode" is set to "aead" the iv is managed and does not need to be set.

mode: Determines how AES_ENCRYPT should behave, with the default mode being a very plain aes-256-cbc implementation and "aead" being an authenticated encryption mode designed to be safer and easier to use.

expires_in: An integer that defines the number of seconds before the ciphertext will be rejected by AES_DECRYPT, this is only available when the mode is set to "aead" and the expiration is enforced by AES_DECRYPT.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
AES_ENCRYPT("hello world", CREDENTIAL.secret_key, mode: "aead", expires_in: 60)
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "65",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_encrypt": "=AES_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key, CREDENTIAL.aes_iv, mode:\"aead\")"
        }
      },
      "position": {
        "x": 1620,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 1725,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_encrypt": "=AES_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key, mode: \"aead\")"
        }
      },
      "position": {
        "x": 1800,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "66",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_encrypt": "=AES_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key, CREDENTIAL.aes_iv, mode:\"aead\", expires_in: 60)"
        }
      },
      "position": {
        "x": 1620,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 1725,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_encrypt": "=AES_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key, mode: \"aead\")"
        }
      },
      "position": {
        "x": 1800,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

### Sample action 3

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_encrypt": "=AES_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key, CREDENTIAL.aes_iv)"
        }
      },
      "position": {
        "x": 1620,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 1725,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "aes_encrypt": "=AES_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key, mode: \"aead\")"
        }
      },
      "position": {
        "x": 1800,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### AND

```
AND(value1, value2, ...)
```

Returns `TRUE` if all arguments are truthy, otherwise returns `FALSE`.

**Categories:** Logic

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
AND(my_action.value>1, my_action.value<100)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
AND(my_action.value>10, my_action.value<100)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value": 5
        }
      },
      "position": {
        "x": -150,
        "y": 1935
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AND",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "and": "=AND(my_action.value>1, my_action.value<100)"
        }
      },
      "position": {
        "x": -225,
        "y": 2010
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AND",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "and": "=AND(my_action.value>5, my_action.value<100)"
        }
      },
      "position": {
        "x": -60,
        "y": 2010
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### APPEND

```
APPEND(text1, text2, ...)
```

Joins two or more pieces of text together.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "/my/fancy/url"
  }
}
```

Formula:

```
APPEND(my_action.message, ".html")
```

Output:

```json
"/my/fancy/url.html"
```

### Example 2: Takes two or more strings as arguments.

Formula:

```
APPEND("app", "end", "ing")
```

Output:

```json
"appending"
```

### Example 3: Alternatively, you can use the concatenation operator:

Formula:

```
"/my/fancy/url" & ".html"
```

Output:

```json
"/my/fancy/url.html"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "APPEND",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "append": "=APPEND(my_action.string1, \" \" & my_action.string2)"
        }
      },
      "position": {
        "x": 180,
        "y": 555
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string1": "I have",
          "string2": "a dog"
        }
      },
      "position": {
        "x": 270,
        "y": 480
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "String Concatenation",
      "description": "Another way to join two strings",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string_concatenation": "=my_action.string1 & \" \" & my_action.string2"
        }
      },
      "position": {
        "x": 360,
        "y": 555
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### ARRAY

```
ARRAY(element1, element2, ...)
```

Returns an Array with each of the arguments as a member.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "three"
  }
}
```

Formula:

```
ARRAY("zero", TRUE, 2, my_action.message)
```

Output:

```json
[
  "zero",
  true,
  2,
  "three"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "element1": "one",
          "element2": 1,
          "element3": false
        }
      },
      "position": {
        "x": 2415,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "ARRAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array": "=ARRAY(my_action.element1, my_action.element2, my_action.element3, NULL)"
        }
      },
      "position": {
        "x": 2415,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### ASIN

```
ASIN(x)
```

Computes the arc sine of x (in radians), returning a number between -π/2 and π/2

**Categories:** Numbers

#### AT_LEAST

```
AT_LEAST(number, minimum)
```

Limits a number to a minimum value.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 4
  }
}
```

Formula:

```
AT_LEAST(my_action.message, 5)
```

Output:

```json
5
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": -4
  }
}
```

Formula:

```
AT_LEAST(my_action.message, 3)
```

Output:

```json
3
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 4
        }
      },
      "position": {
        "x": 2730,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AT_LEAST",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "at_least": "=AT_LEAST(my_action.message, 5)"
        }
      },
      "position": {
        "x": 2640,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AT_LEAST",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "at_least": "=AT_LEAST(my_action.message, 3)"
        }
      },
      "position": {
        "x": 2805,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### AT_MOST

```
AT_MOST(number, maximum)
```

Limits a number to a maximum value.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 4
  }
}
```

Formula:

```
AT_MOST(my_action.message, 5)
```

Output:

```json
4
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 4
  }
}
```

Formula:

```
AT_MOST(my_action.message, 3)
```

Output:

```json
3
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 4
        }
      },
      "position": {
        "x": 3090,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AT_MOST",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "at_most": "=AT_MOST(my_action.message, 5)"
        }
      },
      "position": {
        "x": 3000,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AT_MOST",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "at_most": "=AT_MOST(my_action.message, 3)"
        }
      },
      "position": {
        "x": 3165,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### AVERAGE

```
AVERAGE(array) or AVERAGE(number1, number2, ...)
```

Returns the average (mean) numeric value. Can either be called with a single argument which must be an array or multiple numeric arguments.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      1,
      2
    ]
  }
}
```

Formula:

```
AVERAGE(my_action.message)
```

Output:

```json
1.5
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 1
  }
}
```

Formula:

```
AVERAGE(my_action.message, 2, 3)
```

Output:

```json
2
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "60",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            1,
            2
          ]
        }
      },
      "position": {
        "x": 3075,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AVERAGE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "average": "=AVERAGE(my_action.message)"
        }
      },
      "position": {
        "x": 3075,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "60",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 1
        }
      },
      "position": {
        "x": 3075,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AVERAGE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "average": "=AVERAGE(my_action.message, 2, 3)"
        }
      },
      "position": {
        "x": 3075,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### BASE64URL_DECODE

```
BASE64URL_DECODE(text)
```

Decode URL-safe Base64 encoded text.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "XHVGRkZG"
  }
}
```

Formula:

```
BASE64URL_DECODE(my_action.message)
```

Output:

```json
"\\uFFFF"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "aGVsbG8gd29ybGQ="
        }
      },
      "position": {
        "x": 2340,
        "y": 2070
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "BASE64URL_DECODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "base64url_decode": "=BASE64URL_DECODE(my_action.message)"
        }
      },
      "position": {
        "x": 2340,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### BASE64URL_ENCODE

```
BASE64URL_ENCODE(text)
```

Encode text to URL-safe Base64 encoded text.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "\\uFFFF"
  }
}
```

Formula:

```
BASE64URL_ENCODE(my_action.message)
```

Output:

```json
"XHVGRkZG"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "hello world"
        }
      },
      "position": {
        "x": 2115,
        "y": 2070
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "BASE64URL_ENCODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "base64url_encode": "=BASE64URL_ENCODE(my_action.message)"
        }
      },
      "position": {
        "x": 2115,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### BASE64_DECODE

```
BASE64_DECODE(text, [strict: TRUE])
```

Decode Base64 encoded text. By default this complies with RFC 4648. Set strict: FALSE for RFC 2045 compliant decoding (for example to handle improperly padded base64 strings).

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "SGVsbG8gd29ybGQ="
  }
}
```

Formula:

```
BASE64_DECODE(my_action.message)
```

Output:

```json
"Hello world"
```

### Example 2

Input:

```json
{
  "my_action": {
    "malformed_base64": "SGVsbG8gV29ybGQ=="
  }
}
```

Formula:

```
BASE64_DECODE(my_action.malformed_base64, strict: FALSE)
```

Output:

```json
"Hello World"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "aGVsbG8gd29ybGQ="
        }
      },
      "position": {
        "x": 1860,
        "y": 2070
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "BASE64_DECODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "base64_decode": "=BASE64_DECODE(my_action.message)"
        }
      },
      "position": {
        "x": 1860,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### BASE64_ENCODE

```
BASE64_ENCODE(text_or_object, [strict: TRUE])
```

Encode text or objects using the Base64 encoding algorithm. Objects are automatically converted to JSON strings before encoding. By default this complies with RFC 4648. Set strict: FALSE for RFC 2045 compliant encoding.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello world"
  }
}
```

Formula:

```
BASE64_ENCODE(my_action.message)
```

Output:

```json
"SGVsbG8gd29ybGQ="
```

### Example 2

Input:

```json
{
  "my_action": {
    "object": {
      "message": {
        "body": "Hello World!",
        "from": "Tines",
        "to": "World"
      }
    }
  }
}
```

Formula:

```
BASE64_ENCODE(my_action.object)
```

Output:

```json
"eyJib2R5IjoiSGVsbG8gV29ybGQhIiwiZnJvbSI6IlRpbmVzIiwidG8iOiJXb3JsZCJ9"
```

### Example 3

Input:

```json
{
  "my_action": {
    "long_message": "This is a very long message that will produce base64 output with line breaks when encoded with strict: FALSE"
  }
}
```

Formula:

```
BASE64_ENCODE(my_action.long_message, strict: FALSE)
```

Output:

```json
"VGhpcyBpcyBhIHZlcnkgbG9uZyBtZXNzYWdlIHRoYXQgd2lsbCBwcm9kdWNlIGJhc2U2NCBv\ndXRwdXQgd2l0aCBsaW5lIGJyZWFrcyB3aGVuIGVuY29kZWQgd2l0aCBzdHJpY3Q6IEZBTFNF\n"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "hello world"
        }
      },
      "position": {
        "x": 1635,
        "y": 2070
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "BASE64_ENCODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "base64_encode": "=BASE64_ENCODE(my_action.message)"
        }
      },
      "position": {
        "x": 1635,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### BYTESIZE

```
BYTESIZE(string)
```

Counts the number of bytes in a string.

**Categories:** Text

## Examples

### Example 1

Formula:

```
BYTESIZE("hello")
```

Output:

```json
5
```

### Example 2

Formula:

```
BYTESIZE("😊👍")
```

Output:

```json
8
```

### Example 3

Formula:

```
BYTESIZE("São Paulo")
```

Output:

```json
10
```

#### CAMELIZE

```
CAMELIZE(text)
```

Converts the input to camel case.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "hello_world"
  }
}
```

Formula:

```
CAMELIZE(my_action.message)
```

Output:

```json
"helloWorld"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "foo bar"
  }
}
```

Formula:

```
CAMELIZE(my_action.message)
```

Output:

```json
"fooBar"
```

#### CAPITALIZE

```
CAPITALIZE(text)
```

Makes the first character of text uppercase and converts the remaining characters to lowercase.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
CAPITALIZE(my_action.message)
```

Output:

```json
"Hello world"
```

### Example 2: Only the first character of the text is capitalized. Later words are not capitalized.

Input:

```json
{
  "my_action": {
    "message": "hello WORLD"
  }
}
```

Formula:

```
CAPITALIZE(my_action.message)
```

Output:

```json
"Hello world"
```

## Sample actions

```json
{
  "standardLibVersion": "12",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "this is an example. the capitalize function makes the first character of text uppercase and converts the remaining characters to LOWERCASE."
        }
      },
      "position": {
        "x": 615,
        "y": 330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "CAPITALIZE",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "capitalize": "=CAPITALIZE(my_action.example)"
        }
      },
      "position": {
        "x": 615,
        "y": 450
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### CEIL

```
CEIL(number)
```

Rounds the input up to the nearest whole number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 1.2
  }
}
```

Formula:

```
CEIL(my_action.message)
```

Output:

```json
2
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 2
  }
}
```

Formula:

```
CEIL(my_action.message)
```

Output:

```json
2
```

### Example 3: CEIL will also work on text that only contains a number:

Input:

```json
{
  "my_action": {
    "message": "3.5"
  }
}
```

Formula:

```
CEIL(my_action.message)
```

Output:

```json
4
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": 1.2,
          "value2": 2,
          "value3": "3.5"
        }
      },
      "position": {
        "x": 2625,
        "y": 2055
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "CEIL",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "=CEIL(my_action.value1)",
          "value2": "=CEIL(my_action.value2)",
          "value3": "=CEIL(my_action.value3)"
        }
      },
      "position": {
        "x": 2625,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### CHUNK_ARRAY

```
CHUNK_ARRAY(array, chunk_size)
```

Chunks an array into arrays with chunk_size elements. The last chunk may contain less than chunk_size elements.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
CHUNK_ARRAY(my_action.message, 2)
```

Output:

```json
[
  [
    "north",
    "south"
  ],
  [
    "east",
    "west"
  ]
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "CHUNK ARRAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "chunk_array": "=CHUNK_ARRAY(my_action.flat_array, 2)"
        }
      },
      "position": {
        "x": -6630,
        "y": -1260
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "flat_array": [
            "apple",
            "pear",
            "orange",
            "grapes"
          ]
        }
      },
      "position": {
        "x": -6630,
        "y": -1365
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### CLAMP

```
CLAMP(input, min, max)
```

Restricts a given value to a specific range defined by a minimum and a maximum boundary. If the input value is lower than the minimum, the function returns the minimum. If it is higher than the maximum, it returns the maximum. Otherwise, it returns the input value itself. For string inputs, CLAMP uses lexicographical sort order.

**Categories:** Numbers, Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "input": -10
  }
}
```

Formula:

```
CLAMP(my_action.input, 0, 100)
```

Output:

```json
0
```

### Example 2

Input:

```json
{
  "my_action": {
    "input": "blue"
  }
}
```

Formula:

```
CLAMP(my_action.input, "amber", "green")
```

Output:

```json
"blue"
```

## Sample actions

```json
{
  "standardLibVersion": "89",
  "actionRuntimeVersion": "72",
  "agents": [
    {
      "disabled": false,
      "name": "CLAMP",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "CLAMP_OUTPUT": "=CLAMP(my_action.input, 0, 100)"
        }
      },
      "position": {
        "x": 105,
        "y": 555
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "aiMonitoringThresholds": [],
      "caseConfiguration": {
        "subStatus": null,
        "tags": []
      },
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "templateTags": [],
      "originStoryIdentifier": "cloud:00e45749508fe15ca1af3397eab8db78:18f32aa494b49ac605dcc3aa0eda65cc"
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "input": 12
        }
      },
      "position": {
        "x": 105,
        "y": 450
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "aiMonitoringThresholds": [],
      "caseConfiguration": {
        "subStatus": null,
        "tags": []
      },
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "templateTags": [],
      "originStoryIdentifier": "cloud:00e45749508fe15ca1af3397eab8db78:18f32aa494b49ac605dcc3aa0eda65cc"
    }
  ],
  "sections": [],
  "links": [
    {
      "sourceIdentifier": "1",
      "receiverIdentifier": "0"
    }
  ],
  "diagramNotes": []
}
```

#### COMBINE_COLUMNS_AND_ROWS

```
COMBINE_COLUMNS_AND_ROWS(columns, rows)
```

Combines labeled columnar data into a list of objects. Useful for processing data returned from APIs like Snowflake, Databricks, or Google Sheets.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "cols": [
    "Name",
    "Age"
  ],
  "rows": [
    [
      "Alice",
      35
    ],
    [
      "Bob",
      51
    ]
  ]
}
```

Formula:

```
COMBINE_COLUMNS_AND_ROWS(cols, rows)
```

Output:

```json
[
  {
    "name": "Alice",
    "age": 35
  },
  {
    "name": "Bob",
    "age": 51
  }
]
```

#### COMPACT

```
COMPACT(array)
```

Removes any `null` values from an array.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "one",
      null,
      "three",
      "four",
      null
    ]
  }
}
```

Formula:

```
COMPACT(my_action.message)
```

Output:

```json
[
  "one",
  "three",
  "four"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "COMPACT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "compact": "=COMPACT(my_action.array_with_null)"
        }
      },
      "position": {
        "x": -6675,
        "y": -1380
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array_with_null": [
            "apple",
            null,
            "banana",
            "grape",
            null
          ]
        }
      },
      "position": {
        "x": -6675,
        "y": -1500
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### CONCAT

```
CONCAT(array1, array2, ...)
```

Concatenates (joins together) two or more arrays.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "one",
      "two",
      "three"
    ]
  }
}
```

Formula:

```
CONCAT(my_action.message, ARRAY("four", "five"))
```

Output:

```json
[
  "one",
  "two",
  "three",
  "four",
  "five"
]
```

### Example 2: Takes two or more arrays as arguments.

Formula:

```
CONCAT(ARRAY(1, 2), ARRAY(3, 4), ARRAY(5, 6))
```

Output:

```json
[
  1,
  2,
  3,
  4,
  5,
  6
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "CONCAT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "concat": "=CONCAT(my_action.arr1, my_action.arr2)",
          "add_string": "=CONCAT(my_action.arr1, ARRAY(my_action.string1))"
        }
      },
      "position": {
        "x": -300,
        "y": -6180
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "arr1": [
            "a",
            "b"
          ],
          "arr2": [
            "c",
            "d"
          ],
          "string1": "e"
        }
      },
      "position": {
        "x": -300,
        "y": -6285
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### CONST_EQUALS

```
CONST_EQUALS(string1, string2)
```

Performs a timing-safe comparison of two strings using OpenSSL.fixed_length_secure_compare. Returns true if the strings are identical, false otherwise. If objects that are not strings are passed in, they are stringified before comparison This method helps prevent timing attacks when comparing sensitive values like tokens or signatures. Please note that this formula is not resistant to timing attacks to determine secret length.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "input": "secret_token",
    "expected": "secret_token"
  }
}
```

Formula:

```
CONST_EQUALS(my_action.input, my_action.expected)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "input": "wrong_token",
    "expected": "secret_token"
  }
}
```

Formula:

```
CONST_EQUALS(my_action.input, my_action.expected)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "79",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "input": "secret_token",
          "expected": "secret_token"
        }
      },
      "position": {
        "x": 3075,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "CONST_EQUALS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "result": "=CONST_EQUALS(my_action.input, my_action.expected)"
        }
      },
      "position": {
        "x": 3075,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### CONTAINS_URL

```
CONTAINS_URL(text)
```

Checks if a text value contains a URL. This function returns true if any part of the string matches a URL pattern.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This message contains a url, https://example.com ."
  }
}
```

Formula:

```
CONTAINS_URL(my_action.message)
```

Output:

```json
true
```

#### COS

```
COS(x)
```

Computes the cosine of x (in radians), returning a number between -1 and 1

**Categories:** Numbers

#### COUNTIF

```
COUNTIF(array, comparison)
```

Returns the count of all elements in an array that are equal to the comparison argument.

**Categories:** Arrays, Lambdas

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west",
      "north"
    ]
  }
}
```

Formula:

```
COUNTIF(my_action.message, "north")
```

Output:

```json
2
```

### Example 2: Comparison can also be a lambda.

Input:

```json
{
  "my_action": {
    "message": [
      {
        "count": 6
      },
      {
        "count": 2
      },
      {
        "count": 1
      },
      {
        "count": 3
      }
    ]
  }
}
```

Formula:

```
COUNTIF(my_action.message, LAMBDA(item, item.count > 1))
```

Output:

```json
3
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "COUNTIF",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "count_if": "=COUNTIF(my_action.arr1, 'llama')",
          "count_id_lambda": "=COUNTIF(my_action.arr2, LAMBDA(item, item > 3))\n",
          "counf_if_lambda_2_options": "=COUNTIF(my_action.arr1, LAMBDA(item, OR(item = \"dog\", item = \"cat\")))\n"
        }
      },
      "position": {
        "x": -45,
        "y": -6360
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "arr1": [
            "dog",
            "cat",
            "llama",
            "dog",
            "tiger",
            "dog",
            "llama"
          ],
          "arr2": [
            "1",
            "2",
            "3",
            "4",
            "5",
            "6"
          ]
        }
      },
      "position": {
        "x": -45,
        "y": -6450
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### CSV_PARSE

```
CSV_PARSE(text)
```

Parses CSV-formatted text with auto-detection of the delimiter character and parses output into an array of arrays.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "first, second, third\n1, 2, 3"
  }
}
```

Formula:

```
CSV_PARSE(my_action.message)
```

Output:

```json
[
  [
    "first",
    "second",
    "third"
  ],
  [
    "1",
    "2",
    "3"
  ]
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "CSV_PARSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "csv_parse": "=CSV_PARSE(my_action.message)"
        }
      },
      "position": {
        "x": 2895,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "first, second, third\n1, 2, 3"
        }
      },
      "position": {
        "x": 2895,
        "y": 2055
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### CSV_PARSE_TO_OBJECTS

```
CSV_PARSE_TO_OBJECTS(text)
```

Parses CSV-formatted text with auto-detection of the delimiter character and parses output into an array of objects.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "first, second, third\n1, 2, 3"
  }
}
```

Formula:

```
CSV_PARSE_TO_OBJECTS(my_action.message)
```

Output:

```json
[
  {
    "first": "1",
    "second": "2",
    "third": "3"
  }
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "first, second, third\n1, 2, 3"
        }
      },
      "position": {
        "x": 3120,
        "y": 2055
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "CSV_PARSE_TO_OBJECTS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "csv_parse_to_objects": "=CSV_PARSE_TO_OBJECTS(my_action.message)"
        }
      },
      "position": {
        "x": 3120,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### DATE

```
DATE(date, format, timezone)
```

Takes a date, represented by a string, an integer, or a DATE_PARSE object, and returns a formatted string representation.

The output format is defined in [ruby strftime (Time) syntax](https://docs.ruby-lang.org/en/3.3/strftime_formatting_rdoc.html).

Optionally, the output timezone can be specified with values from the [tz database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).

Natural language parsing is handled by [chronic](https://github.com/mojombo/chronic#examples). When it receives ambiguous dates, ie "01/02/2023", it will default to the EU format (DD/MM/YYYY). Combine with DATE_PARSE to explicitly define the format.

**Categories:** Dates/Times

## Examples

### Example 1

Formula:

```
DATE("2022-07-17", "%a, %b %d, %y")
```

Output:

```json
"Fri, Jul 17, 21"
```

### Example 2

Formula:

```
DATE("2021-07-17", "%Y")
```

Output:

```json
"2021"
```

### Example 3: Takes DATE_PARSE objects

Formula:

```
DATE(DATE_PARSE("05/04/1994", "%d/%m/%Y"), "%B %e, %Y")
```

Output:

```json
"April 5, 1994"
```

### Example 4: Many different representations of dates are supported

Formula:

```
DATE("March 17, 2021", "%b %d, %y")
```

Output:

```json
"Mar 17, 21"
```

### Example 5: Natural language dates are supported. See [chronic](https://github.com/mojombo/chronic#examples) for more examples.

Formula:

```
DATE("fourteenth of May 2024", "%Y-%m-%d")
```

Output:

```json
"2024-05-14"
```

### Example 6: Relative times are supported. See [chronic](https://github.com/mojombo/chronic#examples) for more examples.

Formula:

```
DATE("tomorrow at 10am", "%Y-%m-%d %H:%M:%S")
```

Output:

```json
"2023-05-22 10:00:00"
```

### Example 7: Unix timestamps are supported

Formula:

```
DATE(1647712411, "%Y-%m-%dT%H:%M:%S")
```

Output:

```json
"2022-03-19T17:53:31"
```

### Example 8: Unix timestamps with 13+ digits are treated as milliseconds

Formula:

```
DATE(1687450077063, "%Y-%m-%dT%H:%M:%S.%L")
```

Output:

```json
"2023-06-22T16:07:57.063"
```

### Example 9: To get the current time, pass the keyword `"now"` (or `"today"`)

Formula:

```
DATE("now", "%Y-%m-%d %H:%M")
```

Output:

```json
"2022-03-11 16:24"
```

### Example 10: Defaults to iso8601 format with three decimal places if no format is provided

Formula:

```
DATE("now")
```

Output:

```json
"2022-03-11T16:24:01.123Z"
```

### Example 11: Takes optional timezone argument

Formula:

```
DATE("2023-05-19 12pm", "%Y-%m-%d %H:%M:%S %Z", "Asia/Tokyo")
```

Output:

```json
"2023-05-19 21:00:00 JST"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "DATE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "now": "=DATE('now', '%Y-%m-%d %H:%M:%S')",
          "incoming_alert": "=DATE(my_action.time, '%Y-%m-%d %H:%M:%S')",
          "5_mins_ago": "=DATE(my_action.time, '%s') - 300 |> DATE(%, '%Y-%m-%d %H:%M:%S')"
        }
      },
      "position": {
        "x": -6540,
        "y": -1230
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "time": "2022-11-21 16:00:00"
        }
      },
      "position": {
        "x": -6540,
        "y": -1320
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### DATE_DIFF

```
DATE_DIFF(start_time, end_time, [absolute: TRUE])
```

Returns the precise difference between two given times in terms of years, months, weeks, days, hours, minutes, and seconds. It handles differences across time zones, accounts for leap years, considers months with varying days, and presents both the unit and cumulative differences. When the start time is after the end time the result is returned with absolute values unless the parameter 'absolute' with a value of FALSE is passed in.

**Categories:** Dates/Times

## Examples

### Example 1

Formula:

```
DATE_DIFF('2023-11-03T10:00:00-05:00', '2023-11-03T13:30:45-04:00')
```

Output:

```json
{
  "seconds": 9045,
  "minutes": 150.75,
  "hours": 2.51,
  "days": 0.1,
  "weeks": 0.01,
  "units": {
    "years": 0,
    "months": 0,
    "weeks": 0,
    "days": 0,
    "hours": 2,
    "minutes": 30,
    "seconds": 45
  }
}
```

### Example 2: absolute is TRUE by default, so values are positive when the start time is after the end time

Formula:

```
DATE_DIFF('2023-03-02T11:09:01+0000', '1992-12-18T15:11:01+0000')
```

Output:

```json
{
  "seconds": 953063880,
  "minutes": 15884398,
  "hours": 264739.97,
  "days": 11030.83,
  "weeks": 1575.83,
  "units": {
    "years": 30,
    "months": 2,
    "weeks": 1,
    "days": 4,
    "hours": 19,
    "minutes": 58,
    "seconds": 0
  }
}
```

### Example 3

Formula:

```
DATE_DIFF('2023-03-02T11:09:01+0000', '1992-12-18T15:11:01+0000', absolute: FALSE)
```

Output:

```json
{
  "seconds": -953063880,
  "minutes": -15884398,
  "hours": -264739.97,
  "days": -11030.83,
  "weeks": -1575.83,
  "units": {
    "years": -30,
    "months": -2,
    "weeks": -1,
    "days": -4,
    "hours": -19,
    "minutes": -58,
    "seconds": 0
  }
}
```

## Sample actions

```json
{
  "standardLibVersion": "38",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "DATE_DIFF",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "different_time_zones": "=DATE_DIFF('2023-11-03T10:00:00-05:00', '2023-11-03T13:30:45-04:00')",
          "longer_duration": "=DATE_DIFF('1992-12-18T15:11:01+0000', '2023-03-02T11:09:01+0000')"
        }
      },
      "position": {
        "x": -495,
        "y": 675
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "cloud:da01f7810b72a1e6a5f2ecffe9db1bf1:0aa471f556d31ea505d131f9bca3d099"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### DATE_PARSE

```
DATE_PARSE(date, format, timezone)
```

Parses a date and returns an object representation. Fields include the iso8601 and unix timestamp representations and numerical breakdowns. Useful for parsing dates that would be otherwise ambiguous, like 01/02/2023, before passing them to DATE to be formatted. The date is parsed based on the format string, which uses the [ruby strptime (Time) syntax](https://docs.ruby-lang.org/en/3.3/Time.html#method-c-strptime). If format is omitted, DATE_PARSE attempts to guess the format. Optionally, the timezone can be specified with values from the [tz database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).

Natural language parsing is handled by [chronic](https://github.com/mojombo/chronic#examples).

**Categories:** Dates/Times

## Examples

### Example 1: Parse a date in EU format

Formula:

```
DATE_PARSE("01/02/2023", "%d/%m/%Y")
```

Output:

```json
{
  "iso8601": "2023-02-01T00:00:00+00:00",
  "iso8601_milliseconds": "2023-02-01T00:00:00.000+00:00",
  "unix_timestamp": 1675209600,
  "unix_timestamp_milliseconds": 1675209600000,
  "year": 2023,
  "month": 2,
  "month_name": "February",
  "day": 1,
  "day_of_week": 3,
  "day_name": "Wednesday",
  "hour": 0,
  "minute": 0,
  "second": 0,
  "millisecond": 0,
  "timezone": "UTC",
  "utc_offset": "+00:00",
  "utc_offset_seconds": 0
}
```

### Example 2: Parse a date in US format

Formula:

```
DATE_PARSE("01/02/2023", "%m/%d/%Y")
```

Output:

```json
{
  "iso8601": "2023-01-02T00:00:00+00:00",
  "iso8601_milliseconds": "2023-01-02T00:00:00.000+00:00",
  "unix_timestamp": 1672617600,
  "unix_timestamp_milliseconds": 1672617600000,
  "year": 2023,
  "month": 1,
  "month_name": "January",
  "day": 2,
  "day_of_week": 1,
  "day_name": "Monday",
  "hour": 0,
  "minute": 0,
  "second": 0,
  "millisecond": 0,
  "timezone": "UTC",
  "utc_offset": "+00:00",
  "utc_offset_seconds": 0
}
```

### Example 3: Parse a date in a specific timezone

Formula:

```
DATE_PARSE("2022-01-01 12:00:00", "%Y-%m-%d %H:%M:%S", "America/Los_Angeles")
```

Output:

```json
{
  "iso8601": "2022-01-01T12:00:00-08:00",
  "iso8601_milliseconds": "2022-01-01T12:00:00.000-08:00",
  "unix_timestamp": 1641067200,
  "unix_timestamp_milliseconds": 1641067200000,
  "year": 2022,
  "month": 1,
  "month_name": "January",
  "day": 1,
  "day_of_week": 6,
  "day_name": "Saturday",
  "hour": 12,
  "minute": 0,
  "second": 0,
  "millisecond": 0,
  "timezone": "PST",
  "utc_offset": "-08:00",
  "utc_offset_seconds": -28800
}
```

### Example 4: Can be passed to DATE

Formula:

```
DATE(DATE_PARSE("05/04/1994", "%d/%m/%Y"), "%B %e, %Y")
```

Output:

```json
"April 5, 1994"
```

### Example 5: Grab specific fields via dot-notation

Formula:

```
DATE_PARSE("01/02/202", "%m/%d/%Y").day_name
```

Output:

```json
"Monday"
```

### Example 6: If no format is provided, DATE_PARSE will attempt to guess the format, just like DATE

Formula:

```
DATE_PARSE("March 17, 2021").year
```

Output:

```json
2021
```

### Example 7: Accepts unix timestamps, just like DATE

Formula:

```
DATE_PARSE(1647712411).iso8601
```

Output:

```json
"2022-03-19T17:53:31+00:00"
```

### Example 8: Accepts unix timestamps in milliseconds, just like DATE

Formula:

```
DATE_PARSE(1687450077063).iso8601_milliseconds
```

Output:

```json
"2023-06-22T16:07:57.063+00:00"
```

### Example 9: Use "now" or "today" to get the current time, just like DATE

Formula:

```
DATE_PARSE("now").unix_timestamp_milliseconds
```

Output:

```json
1692791703233
```

#### DAY

```
DAY(date)
```

Returns the day of the month for the specified date. Accepted Format: year-month-day (year/month/day)

**Categories:** Dates/Times

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "2022-03-19T17:49:01+0000"
  }
}
```

Formula:

```
DAY(my_action.message)
```

Output:

```json
"19"
```

## Sample actions

```json
{
  "standardLibVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "2022-03-19T17:49:01+0000"
        }
      },
      "position": {
        "x": 1035,
        "y": 1080
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "reccrdters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "DAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "day": "=DAY(my_action.message)"
        }
      },
      "position": {
        "x": 1035,
        "y": 1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recrecdrds": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### DEEP_MERGE

```
DEEP_MERGE(object, object, ...)
```

Creates a new object by recursively merging two or more objects together. Where there are key collisions that last value is used.

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "object_1": {
    "location": {
      "city": "Dublin"
    }
  },
  "object_2": {
    "location": {
      "country": "Ireland"
    }
  }
}
```

Formula:

```
DEEP_MERGE(object_1, object_2)
```

Output:

```json
{
  "location": {
    "city": "Dublin",
    "country": "Ireland"
  }
}
```

### Example 2

Input:

```json
{
  "object_1": {
    "location": {
      "city": "Dublin"
    }
  },
  "object_2": {
    "location": {
      "city": "Cork"
    }
  }
}
```

Formula:

```
DEEP_MERGE(object_1, object_2)
```

Output:

```json
{
  "location": {
    "city": "Cork"
  }
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "DEEP MERGE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "deep_merge": "=DEEP_MERGE(my_action.obj4, my_action.obj5)"
        }
      },
      "position": {
        "x": -6555,
        "y": -1380
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "obj4": {
            "vegetable": "carrot",
            "nested": {
              "weather": "rain"
            }
          },
          "obj5": {
            "city": "Dublin",
            "nested": {
              "country": "Ireland"
            }
          }
        }
      },
      "position": {
        "x": -6555,
        "y": -1485
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### DEFAULT

```
DEFAULT(value, fallback1, fallback2, ...)
```

Allows you to specify a fallback in case a value doesn’t exist.

Returns the fallback value if the input is `null`, `false`, or an empty text value, array, or object.

**Categories:** Logic, Arrays, Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": []
  }
}
```

Formula:

```
DEFAULT(my_action.message, "fallback")
```

Output:

```json
"fallback"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": [
      1
    ]
  }
}
```

Formula:

```
DEFAULT(my_action.message, "fallback")
```

Output:

```json
[
  1
]
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": ""
  }
}
```

Formula:

```
DEFAULT(my_action.message, "fallback")
```

Output:

```json
"fallback"
```

### Example 4

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
DEFAULT(my_action.message, "fallback")
```

Output:

```json
"hello world"
```

### Example 5

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
DEFAULT(my_action.does_not_exist, "fallback")
```

Output:

```json
"fallback"
```

### Example 6

Input:

```json
{
  "my_action": {
    "message": false
  }
}
```

Formula:

```
DEFAULT(my_action.message, "fallback")
```

Output:

```json
"fallback"
```

### Example 7

Input:

```json
{
  "my_action": {
    "message": [],
    "fallback": ""
  }
}
```

Formula:

```
DEFAULT(my_action.message, my_action.fallback, "fallback")
```

Output:

```json
"fallback"
```

## Sample actions

```json
{
  "standardLibVersion": "12",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "A valuable is present"
        }
      },
      "position": {
        "x": 435,
        "y": 300
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "DEFAULT",
      "description": "If the response is empty, then it will go based on your hard-coded value, in this case 'if not'.",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "default": "=DEFAULT(my_action.example, 'if not')"
        }
      },
      "position": {
        "x": 435,
        "y": 420
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "This example is missing a value to show the different result.",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": ""
        }
      },
      "position": {
        "x": 585,
        "y": 300
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### DELETE

```
DELETE(array, index)
```

Deletes an item from an array at a given index and returns the array minus that deleted value.

**Categories:** Arrays

## Examples

### Example 1

Formula:

```
DELETE(["a", "b", "c"], 1)
```

Output:

```json
[
  "a",
  "c"
]
```

### Example 2: Accepts negative indexes.

Formula:

```
DELETE(["a", "b", "c"], -1)
```

Output:

```json
[
  "a",
  "b"
]
```

## Sample actions

```json
{
  "standardLibVersion": "38",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array": [
            "hello",
            "test",
            "1",
            "b",
            "a"
          ]
        }
      },
      "position": {
        "x": 975,
        "y": 180
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "cloud:8b3d0c1d536d7aef6416b3d0e57a460a:6c8cd17db8bb0a3868840ff9d61a890b"
    },
    {
      "disabled": false,
      "name": "DELETE",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "delete": "=DELETE(my_action.array, 2)"
        }
      },
      "position": {
        "x": 975,
        "y": 300
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "cloud:8b3d0c1d536d7aef6416b3d0e57a460a:6c8cd17db8bb0a3868840ff9d61a890b"
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### DIFFERENCE

```
DIFFERENCE(array1, array2)
```

Returns an array of items that appear in the first array but not in the second array

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "array1": [
      1,
      2,
      3
    ],
    "array2": [
      1,
      2
    ]
  }
}
```

Formula:

```
DIFFERENCE(my_action.array1, my_action.array2)
```

Output:

```json
[
  3
]
```

## Sample actions

```json
{
  "standardLibVersion": "29",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "my action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array1": [
            "1",
            "2",
            "3"
          ],
          "array2": [
            "1",
            "2",
            "4"
          ]
        }
      },
      "position": {
        "x": 2265,
        "y": -16230
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "kes-test:779db79df723796bdf6e92d1f6bfc714"
    },
    {
      "disabled": false,
      "name": "DIFFERENCE",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array1_vs_array2": "=DIFFERENCE(my_action.array1, my_action.array2)",
          "array2_vs_array1": "=DIFFERENCE(my_action.array2, my_action.array1)",
          "total_differences": "=CONCAT(DIFFERENCE(my_action.array1, my_action.array2), DIFFERENCE(my_action.array2, my_action.array1))"
        }
      },
      "position": {
        "x": 2265,
        "y": -16140
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "kes-test:779db79df723796bdf6e92d1f6bfc714"
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### DISTANCE_OF_TIME_IN_WORDS

```
DISTANCE_OF_TIME_IN_WORDS(date, [from])
```

Returns the distance between two times in words for the provided date/time. Defaults to the current time if "from" is unspecified.
Distances for seconds below 1 minute and 29 seconds are reported based on the following table:

0-4   secs      # => less than 5 seconds
5-9   secs      # => less than 10 seconds
10-19 secs      # => less than 20 seconds
20-39 secs      # => half a minute
40-59 secs      # => less than a minute
60-89 secs      # => 1 minute

**Categories:** Dates/Times

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "in_the_past": "2022-03-19T17:49:01+0000"
  }
}
```

Formula:

```
DISTANCE_OF_TIME_IN_WORDS(my_action.in_the_past)
```

Output:

```json
"over 1 year ago"
```

### Example 2

Input:

```json
{
  "my_action": {
    "in_future": "2023-11-19T17:49:01+0000"
  }
}
```

Formula:

```
DISTANCE_OF_TIME_IN_WORDS(my_action.in_future)
```

Output:

```json
"in 24 days"
```

### Example 3

Input:

```json
{
  "my_action": {
    "from_in_future": "2024-02-01T17:01:01+0000"
  }
}
```

Formula:

```
DISTANCE_OF_TIME_IN_WORDS('2024-02-01T20:31:01+0000', my_action.from_in_future)
```

Output:

```json
"in about 4 hours"
```

## Sample actions

```json
{
  "standardLibVersion": "37",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "my action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "in_the_past": "2022-03-19T17:49:01+0000",
          "in_future": "2023-11-19T17:49:01+0000"
        }
      },
      "position": {
        "x": -150,
        "y": 150
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "kes-test:779db79df723796bdf6e92d1f6bfc714"
    },
    {
      "disabled": false,
      "name": "DISTANCE_OF_TIME_IN_WORDS",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "in_the_past": "=DISTANCE_OF_TIME_IN_WORDS(my_action.in_the_past)",
          "in_future": "<<DISTANCE_OF_TIME_IN_WORDS(my_action.in_future)>>",
          "in_future_relative_to_2024": "<<DISTANCE_OF_TIME_IN_WORDS(\"2024-02-01T15:01:01+0000\", \"2024-01-01T15:01:01+0000\")>>",
          "in_future_relative_to_2024_in_hours": "<<DISTANCE_OF_TIME_IN_WORDS(\"2024-02-01T20:31:01+0000\", \"2024-02-01T17:01:01+0000\")>>"
        }
      },
      "position": {
        "x": -150,
        "y": 240
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "kes-test:779db79df723796bdf6e92d1f6bfc714"
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### DIVIDED_BY

```
DIVIDED_BY(number, denominator)
```

Divides a number by the specified number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 16
  }
}
```

Formula:

```
DIVIDED_BY(my_action.message, 4)
```

Output:

```json
4
```

### Example 2: If you divide by a whole number, the result is rounded down to a whole number:

Input:

```json
{
  "my_action": {
    "message": 5
  }
}
```

Formula:

```
DIVIDED_BY(my_action.message, 3)
```

Output:

```json
1
```

### Example 3: If you divide by a number with decimal places, the result will have decimal places:

Input:

```json
{
  "my_action": {
    "message": 5
  }
}
```

Formula:

```
DIVIDED_BY(my_action.message, 3.0)
```

Output:

```json
1.666666666666667
```

### Example 4: You can multiply the denominator by 1.0 to force the result to have decimal places:

Input:

```json
{
  "my_action": {
    "message": 3
  }
}
```

Formula:

```
DIVIDED_BY(5, my_action.message * 1.0)
```

Output:

```json
1.666666666666667
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "16",
          "value2": "5",
          "value3": "5",
          "value4": "3"
        }
      },
      "position": {
        "x": 1635,
        "y": 2355
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "DIVIDED_BY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "=DIVIDED_BY(my_action.value1, 4)",
          "value2": "=DIVIDED_BY(my_action.value2, 3)",
          "value3": "=DIVIDED_BY(my_action.value3, 3.0)",
          "value4": "=DIVIDED_BY(5, my_action.value4 * 1.0)"
        }
      },
      "position": {
        "x": 1635,
        "y": 2430
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### DOWNCASE

```
DOWNCASE(text)
```

Makes each character in text lowercase.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello World"
  }
}
```

Formula:

```
DOWNCASE(my_action.message)
```

Output:

```json
"hello world"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "already lowercase"
  }
}
```

Formula:

```
DOWNCASE(my_action.message)
```

Output:

```json
"already lowercase"
```

## Sample actions

```json
{
  "standardLibVersion": "12",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "THIS WILL ALL BECOME LOWERCASE"
        }
      },
      "position": {
        "x": 1365,
        "y": 285
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "DOWNCASE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "downcase": "=DOWNCASE(my_action.example)"
        }
      },
      "position": {
        "x": 1365,
        "y": 405
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### EML_PARSE

```
EML_PARSE(text, [single_body: TRUE])
```

Takes text containing EML (RFC822) content and parses out information such as `to`, `from`, `subject`, `attachments`, etc.

By default the parsed email includes a single `body` field containing the HTML part if available, otherwise the text part. Set the single_body optional parameter to FALSE to replace `body` with separate `body_html` and `body_text` fields.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Delivered-To: john@example.com\nDate: Fri, 20 Jan 2023 00:00:37 +0000\nFrom: John Smith john@example.com\nReply-To:john@example.com\nTo: john@example.com\nMessage-ID: <message_id>\nSubject: Example email\nMime-Version: 1.0\nContent-Type: multipart/alternative;\n boundary=\"--==_mimepart_63c9d9a544871_73eb214182db\";\n charset=UTF-8\nContent-Transfer-Encoding: 7bit\n\n\n----==_mimepart_63c9d9a544871_73eb214182db\nContent-Type: text/plain;\n charset=UTF-8\nContent-Transfer-Encoding: 7bit\n\nA real email body could go here\n----==_mimepart_63c9d9a544871_73eb214182db\nContent-Type: text/html;\n charset=UTF-8\nContent-Transfer-Encoding: 7bit\n\n<!DOCTYPE html>\n<html>\n <head>\n <meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\" />\n </head>\n <body>\n A real email body could go here\n </body>\n</html>\n\n----==_mimepart_63c9d9a544871_73eb214182db--"
  }
}
```

Formula:

```
EML_PARSE(my_action.message)
```

Output:

```json
{
  "attachments": [],
  "body": "<!DOCTYPE html>\n<html>\n <head>\n <meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\" />\n </head>\n <body>\n A real email body could go here\n </body>\n</html>\n",
  "cc": [],
  "date": "2023-01-20T00:00:37+00:00",
  "from": "John Smith john@example.com",
  "headers": {
    "Content-Transfer-Encoding": "7bit",
    "Content-Type": "multipart/alternative; boundary=\"--==_mimepart_63c9d9a544871_73eb214182db\"; charset=UTF-8",
    "Date": "Fri, 20 Jan 2023 00:00:37 +0000",
    "Delivered-To": "john@example.com",
    "From": "John Smith john@example.com",
    "Message-ID": "<message_id>",
    "Mime-Version": "1.0",
    "Reply-To": "john@example.com",
    "Subject": "Example email",
    "To": "john@example.com"
  },
  "message_id": "message_id",
  "subject": "Example email",
  "to": [
    "john@example.com"
  ]
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Delivered-To: john@example.com\nDate: Fri, 20 Jan 2023 00:00:37 +0000\nFrom: John Smith john@example.com\nReply-To:john@example.com\nTo: john@example.com\nMessage-ID: <message_id>\nSubject: Example email\nMime-Version: 1.0\nContent-Type: multipart/alternative;\n boundary=\"--==_mimepart_63c9d9a544871_73eb214182db\";\n charset=UTF-8\nContent-Transfer-Encoding: 7bit\n\n\n----==_mimepart_63c9d9a544871_73eb214182db\nContent-Type: text/plain;\n charset=UTF-8\nContent-Transfer-Encoding: 7bit\n\nA real email body could go here\n----==_mimepart_63c9d9a544871_73eb214182db\nContent-Type: text/html;\n charset=UTF-8\nContent-Transfer-Encoding: 7bit\n\n<!DOCTYPE html>\n<html>\n <head>\n <meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\" />\n </head>\n <body>\n A real email body could go here\n </body>\n</html>\n\n----==_mimepart_63c9d9a544871_73eb214182db--"
        }
      },
      "position": {
        "x": -210,
        "y": 3390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "EML_PARSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "eml_parse": "=EML_PARSE(my_action.message)"
        }
      },
      "position": {
        "x": -210,
        "y": 3480
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### ENDS_WITH

```
ENDS_WITH(text, suffix)
```

Returns true if the text ends with the suffix

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
ENDS_WITH(my_action.message, "World!")
```

Output:

```json
"true"
```

### Example 2: case-sensitive

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
ENDS_WITH(my_action.message, "world!")
```

Output:

```json
"false"
```

#### ERROR

```
ERROR(message)
```

Raises an error with the specified message.

**Categories:** Logic

## Examples

### Example 1

Formula:

```
ERROR()
```

Output:

```json
"Error: Explicit error raised"
```

### Example 2

Formula:

```
ERROR('My error message')
```

Output:

```json
"Error: My error message"
```

### Example 3: Useful for validating input values

Input:

```json
{
  "action": {
    "string_value": 0
  }
}
```

Formula:

```
IF(TYPE(action.string_value) = "String", action.string_value, ERROR("string_value must be a string, got " & TYPE(action.string_value)))
```

Output:

```json
"Error: string_value must be a string, got Integer"
```

#### ESCAPE

```
ESCAPE(text)
```

Escapes text by replacing characters with escape sequences (e.g. so that the text can be used in a URL)

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Did you enjoy 'Willy Wonka & the Chocolate Factory'?"
  }
}
```

Formula:

```
ESCAPE(my_action.message)
```

Output:

```json
"Did you enjoy &#39;Willy Wonka &amp; the Chocolate Factory&#39;?"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "Hello World"
  }
}
```

Formula:

```
ESCAPE(my_action.message)
```

Output:

```json
"Hello World"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Did you enjoy 'Willy Wonka & the Chocolate Factory'?"
        }
      },
      "position": {
        "x": 1845,
        "y": 2355
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "ESCAPE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "escape": "<<ESCAPE(my_action.message)>>"
        }
      },
      "position": {
        "x": 1935,
        "y": 2445
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Hello World"
        }
      },
      "position": {
        "x": 2040,
        "y": 2355
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### ESCAPE_ONCE

```
ESCAPE_ONCE(text)
```

Escapes text without changing existing escaped entities.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "a > b & c"
  }
}
```

Formula:

```
ESCAPE_ONCE(my_action.message)
```

Output:

```json
"a &gt; b &amp; c"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "a &gt; b & c"
  }
}
```

Formula:

```
ESCAPE_ONCE(my_action.message)
```

Output:

```json
"a &gt; b &amp; c"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "a > b > c"
        }
      },
      "position": {
        "x": -915,
        "y": -6405
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "reccrdters": [],
      "form": null
    },
    {
      "disabled": false,
      "name": "ESCAPE_ONCE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "escape_once": "<<ESCAPE_ONCE(my_action.message)>>"
        }
      },
      "position": {
        "x": -915,
        "y": -6315
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recrecdrds": [],
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### ESTIMATED_TOKEN_COUNT

```
ESTIMATED_TOKEN_COUNT(input)
```

Estimates the number of tokens which would represent the input data when passed to a Claude 3 LLM. The token count is an estimate based on an older Claude tokenizer and will not be an exact match for the tokens used.

The formula will error if the input contains more than 1 million characters.

**Categories:** Text, Objects, Arrays, Numbers

## Examples

### Example 1

Formula:

```
ESTIMATED_TOKEN_COUNT('Hi! Welcome to Tines')
```

Output:

```json
6
```

### Example 2

Input:

```json
{
  "fetch_items": {
    "data": [
      {
        "finding": "A",
        "severity": "High"
      },
      {
        "finding": "B",
        "severity": "Medium"
      },
      {
        "finding": "C",
        "severity": "Low"
      }
    ]
  }
}
```

Formula:

```
ESTIMATED_TOKEN_COUNT(fetch_items.data)
```

Output:

```json
28
```

## Sample actions

```json
{
  "standardLibVersion": "56",
  "actionRuntimeVersion": "12",
  "agents": [
    {
      "disabled": false,
      "name": "Get token count",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "count": "=ESTIMATED_TOKEN_COUNT('Hi! Welcome to Tines')"
        }
      },
      "position": {
        "x": 2220,
        "y": 705
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "caseConfiguration": {
        "subStatus": null
      },
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "cloud:b1120da8923a44bfb8d4c7c31252441d:7ebe309e78ac98c1b94d0a4f071c4e50"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### FILTER

```
FILTER(array | object, values_to_keep | LAMBDA(arg1, [arg2], expr))
```

Filters an array or an object by a lambda function or an array of values to keep.

If the target is an array, the lambda must take one argument: the value.

If the target is an object, the lambda can take one or two arguments. If the lambda takes one argument, the argument is the value. If the lambda takes two arguments, the first argument is the key and the second argument is the value.

**Categories:** Lambdas, Arrays, Objects

## Examples

### Example 1

Input:

```json
{
  "my_array": [
    1,
    1,
    2,
    2,
    3,
    3
  ]
}
```

Formula:

```
FILTER(my_array, ARRAY(2, 4))
```

Output:

```json
[
  2,
  2
]
```

### Example 2

Input:

```json
{
  "my_array": [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10
  ]
}
```

Formula:

```
FILTER(my_array, LAMBDA(item, item > 5))
```

Output:

```json
[
  6,
  7,
  8,
  9,
  10
]
```

### Example 3

Input:

```json
{
  "my_object": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4,
    "e": 5
  }
}
```

Formula:

```
FILTER(my_object, ARRAY(2, 4))
```

Output:

```json
{
  "b": 2,
  "d": 4
}
```

### Example 4: If the target is an object and the lambda takes one argument, the argument is the value.

Input:

```json
{
  "my_object": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4,
    "e": 5
  }
}
```

Formula:

```
FILTER(my_object, LAMBDA(value, value > 2))
```

Output:

```json
{
  "c": 3,
  "d": 4,
  "e": 5
}
```

### Example 5: If the target is an object and the lambda takes two arguments, the first argument is the key and the second argument is the value.

Input:

```json
{
  "my_object": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4,
    "e": 5
  }
}
```

Formula:

```
FILTER(my_object, LAMBDA(key, value, key = 'a' || value > 3))
```

Output:

```json
{
  "a": 1,
  "d": 4,
  "e": 5
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "FILTER",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "filter": "=FILTER(my_action.object_array, LAMBDA(object, INCLUDES(object.name, 'Martin')))"
        }
      },
      "position": {
        "x": -6630,
        "y": -1425
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object_array": [
            {
              "name": "Martin"
            },
            {
              "name": "Shelby"
            },
            {
              "name": "Thomas"
            }
          ]
        }
      },
      "position": {
        "x": -6630,
        "y": -1530
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### FIND

```
FIND(array, LAMBDA(item, [expr]))
```

Returns the first element in the array for which the lambda returns true.

**Categories:** Lambdas, Arrays

## Examples

### Example 1

Input:

```json
{
  "my_array": [
    {
      "animal": "bee",
      "type": "insect"
    },
    {
      "animal": "cat",
      "type": "mammal"
    },
    {
      "animal": "dog",
      "type": "mammal"
    }
  ]
}
```

Formula:

```
FIND(my_array, LAMBDA(item, item.type = 'mammal'))
```

Output:

```json
{
  "animal": "cat",
  "type": "mammal"
}
```

### Example 2

Input:

```json
{
  "my_array": [
    {
      "animal": "bee",
      "type": "insect"
    },
    {
      "animal": "cat",
      "type": "mammal"
    },
    {
      "animal": "dog",
      "type": "mammal"
    }
  ]
}
```

Formula:

```
FIND(my_array, LAMBDA(item, item.type = 'fish'))
```

Output:

```json
null
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "my_array": [
            {
              "animal": "bee",
              "type": "insect"
            },
            {
              "animal": "cat",
              "type": "mammal"
            },
            {
              "animal": "dog",
              "type": "mammal"
            }
          ]
        }
      },
      "position": {
        "x": 2655,
        "y": 2385
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "FIND",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "find": "=FIND(my_action.my_array, LAMBDA(item, item.type = 'mammal'))"
        }
      },
      "position": {
        "x": 2655,
        "y": 2460
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### FIRST

```
FIRST(array)
```

Returns the first element of an array.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
FIRST(my_action.message)
```

Output:

```json
"north"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "flat_array": [
            "apple",
            "pear",
            "orange",
            "grapes"
          ],
          "object_array": [
            {
              "name": "Martin"
            },
            {
              "name": "Shelby"
            },
            {
              "name": "Thomas"
            }
          ]
        }
      },
      "position": {
        "x": -6540,
        "y": -1350
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "FIRST SECOND ETC",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "first": "=FIRST(my_action.object_array)",
          "second": "<<my_action.flat_array[1]>>"
        }
      },
      "position": {
        "x": -6540,
        "y": -1260
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### FLATTEN

```
FLATTEN(array)
```

Turn nested arrays into a single 1 dimensional array

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": [
    [
      "dog",
      "cat",
      "turtle"
    ],
    [
      "deer",
      "bear",
      "tiger"
    ]
  ]
}
```

Formula:

```
FLATTEN(my_action)
```

Output:

```json
[
  "dog",
  "cat",
  "turtle",
  "deer",
  "bear",
  "tiger"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "FLATTEN",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "flatten": "=FLATTEN(my_action.nested_array)"
        }
      },
      "position": {
        "x": -6540,
        "y": -1275
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "nested_array": [
            [
              "cat",
              "turtle"
            ],
            [
              "dog"
            ]
          ]
        }
      },
      "position": {
        "x": -6540,
        "y": -1380
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### FLATTEN_JSON

```
FLATTEN_JSON(object, [separator])
```

Flattens nested JSON into a new object with a single layer of key/value pairs. Default key separator is a period or full stop (`.`)

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "contact": {
    "address": [
      {
        "city": "Huntsville",
        "geo": {
          "lat": "2.243232",
          "lon": "1.23123"
        },
        "state": "NC",
        "street": "101 3rd St",
        "type": "home"
      },
      {
        "city": "city",
        "geo": {
          "lat": "2.243232",
          "lon": "1.23123"
        },
        "state": "NC",
        "street": "15 Main St",
        "type": "work"
      }
    ],
    "id": "557",
    "name": "Jane Smith"
  }
}
```

Formula:

```
FLATTEN_JSON(json)
```

Output:

```json
{
  "contact.address.0.city": "Huntsville",
  "contact.address.0.geo.lat": "2.243232",
  "contact.address.0.geo.lon": "1.23123",
  "contact.address.0.state": "NC",
  "contact.address.0.street": "101 3rd St",
  "contact.address.0.type": "home",
  "contact.address.1.city": "city",
  "contact.address.1.geo.lat": "2.243232",
  "contact.address.1.geo.lon": "1.23123",
  "contact.address.1.state": "NC",
  "contact.address.1.street": "15 Main St",
  "contact.address.1.type": "work",
  "contact.id": "557",
  "contact.name": "Jane Smith"
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "contact": {
            "address": [
              {
                "city": "Huntsville",
                "geo": {
                  "lat": "2.243232",
                  "lon": "1.23123"
                },
                "state": "NC",
                "street": "101 3rd St",
                "type": "home"
              },
              {
                "city": "city",
                "geo": {
                  "lat": "2.243232",
                  "lon": "1.23123"
                },
                "state": "NC",
                "street": "15 Main St",
                "type": "work"
              }
            ],
            "id": "557",
            "name": "Jane Smith"
          }
        }
      },
      "position": {
        "x": 2895,
        "y": 2385
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "FLATTEN_JSON",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "flatten_json": "=FLATTEN_JSON(my_action)"
        }
      },
      "position": {
        "x": 2895,
        "y": 2475
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### FLOOR

```
FLOOR(number)
```

Rounds a number down to the nearest whole number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 1.2
  }
}
```

Formula:

```
FLOOR(my_action.message)
```

Output:

```json
1
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 2
  }
}
```

Formula:

```
FLOOR(my_action.message)
```

Output:

```json
2
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": 67.28
  }
}
```

Formula:

```
FLOOR(my_action.message)
```

Output:

```json
67
```

### Example 4: FLOOR will also work on text that only contains a number:

Input:

```json
{
  "my_action": {
    "message": "3.5"
  }
}
```

Formula:

```
FLOOR(my_action.message)
```

Output:

```json
3
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": 1.2,
          "value2": 2,
          "value3": 67.28,
          "value4": "3.5"
        }
      },
      "position": {
        "x": 3165,
        "y": 2370
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "FLOOR",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "=FLOOR(my_action.value1)",
          "value2": "=FLOOR(my_action.value2)",
          "value3": "=FLOOR(my_action.value3)",
          "value4": "=FLOOR(my_action.value4)"
        }
      },
      "position": {
        "x": 3165,
        "y": 2475
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### FORCE_ARRAY

```
FORCE_ARRAY(anything)
```

If the supplied argument is an array it returns it, otherwise wraps it in an array

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "a string"
  }
}
```

Formula:

```
FORCE_ARRAY(my_action.message)
```

Output:

```json
[
  "a string"
]
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "Hello World"
  }
}
```

Formula:

```
FORCE_ARRAY(my_action.missing_key)
```

Output:

```json
[]
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": [
      "1",
      "2",
      "3"
    ]
  }
}
```

Formula:

```
FORCE_ARRAY(my_action.message)
```

Output:

```json
[
  "1",
  "2",
  "3"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "a string",
          "value2": "Hello World",
          "value3": [
            "\"1\"",
            "\"2\"",
            "\"3\""
          ]
        }
      },
      "position": {
        "x": 1635,
        "y": 2625
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "FORCE_ARRAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "=FORCE_ARRAY(my_action.value1)",
          "value2": "=FORCE_ARRAY(my_action.value2.missing_key)",
          "value3": "=FORCE_ARRAY(my_action.value3)"
        }
      },
      "position": {
        "x": 1635,
        "y": 2730
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### GENERATE_RSA_KEYS

```
GENERATE_RSA_KEYS()
```

Generates a pair of public and private keys using the RSA encryption algorithm. The key size can be either 2048 or 4096, and the default is 2048.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
GENERATE_RSA_KEYS()
```

Output:

```json
[
  "public_key",
  "private_key"
]
```

## Sample actions

```json
{
  "standardLibVersion": "20",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "Event Transform Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "=GENERATE_RSA_KEYS()"
        }
      },
      "position": {
        "x": 1230,
        "y": -510
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### GET

```
GET(object, path)
```

Get the value in object at the specified key or path. If a property name has spaces, wrap it in square brackets and quotes.

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "ab": "c"
  }
}
```

Formula:

```
GET(my_action, "ab")
```

Output:

```json
"c"
```

### Example 2

Input:

```json
{
  "my_action": {
    "a": {
      "b": {
        "c": "d"
      }
    }
  }
}
```

Formula:

```
GET(my_action, "a.b.c")
```

Output:

```json
"d"
```

### Example 3

Input:

```json
{
  "fetch_data": {
    "user": {
      "User ID": "ID1"
    }
  }
}
```

Formula:

```
GET(fetch_data, 'user["User ID"]')
```

Output:

```json
"ID1"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "ab": "c",
          "a/b": "c"
        }
      },
      "position": {
        "x": 1905,
        "y": 2625
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "GET",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "get": "=GET(my_action, \"ab\")"
        }
      },
      "position": {
        "x": 1815,
        "y": 2730
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "GET",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "get": "=GET(my_action, \"[\\\"a/b\\\"]\")"
        }
      },
      "position": {
        "x": 1965,
        "y": 2730
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### GROUP_BY

```
GROUP_BY(items, key_or_lambda, [as_array: FALSE])
```

Groups an array of objects by a key or the result of a `LAMBDA` function. `as_array` is a boolean keyword argument (`TRUE` / `FALSE`, default `FALSE`). When `as_array` is `TRUE`, `GROUP_BY` returns an array of objects with `key` and `values` properties. When `as_array` is `FALSE`, it returns an object keyed by group.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "employees": [
    {
      "name": "Alice",
      "office": "Dublin"
    },
    {
      "name": "Bob",
      "office": "Dublin"
    },
    {
      "name": "Charlie",
      "office": "New York"
    },
    {
      "name": "Dave",
      "office": "New York"
    }
  ]
}
```

Formula:

```
GROUP_BY(employees, "office")
```

Output:

```json
{
  "Dublin": [
    {
      "name": "Alice",
      "office": "Dublin"
    },
    {
      "name": "Bob",
      "office": "Dublin"
    }
  ],
  "New York": [
    {
      "name": "Charlie",
      "office": "New York"
    },
    {
      "name": "Dave",
      "office": "New York"
    }
  ]
}
```

### Example 2

Input:

```json
{
  "alerts": [
    {
      "name": "Failed Login",
      "severity": 8,
      "source": "firewall"
    },
    {
      "name": "Config Change",
      "severity": 5,
      "source": "audit"
    },
    {
      "name": "Disk Space Low",
      "severity": 3,
      "source": "monitoring"
    },
    {
      "name": "Malware Detected",
      "severity": 9,
      "source": "endpoint"
    }
  ]
}
```

Formula:

```
GROUP_BY(alerts, LAMBDA(alert, IF(alert['severity'] > 7, 'high', IF(alert['severity'] > 4, 'medium', 'low'))))
```

Output:

```json
{
  "high": [
    {
      "name": "Failed Login",
      "severity": 8,
      "source": "firewall"
    },
    {
      "name": "Malware Detected",
      "severity": 9,
      "source": "endpoint"
    }
  ],
  "medium": [
    {
      "name": "Config Change",
      "severity": 5,
      "source": "audit"
    }
  ],
  "low": [
    {
      "name": "Disk Space Low",
      "severity": 3,
      "source": "monitoring"
    }
  ]
}
```

### Example 3

Input:

```json
{
  "employees": [
    {
      "name": "Alice",
      "office": "Dublin"
    },
    {
      "name": "Bob",
      "office": "Dublin"
    },
    {
      "name": "Charlie",
      "office": "New York"
    },
    {
      "name": "Dave",
      "office": "New York"
    }
  ]
}
```

Formula:

```
GROUP_BY(employees, 'office', as_array: TRUE)
```

Output:

```json
[
  {
    "key": "Dublin",
    "values": [
      {
        "name": "Alice",
        "office": "Dublin"
      },
      {
        "name": "Bob",
        "office": "Dublin"
      }
    ]
  },
  {
    "key": "New York",
    "values": [
      {
        "name": "Charlie",
        "office": "New York"
      },
      {
        "name": "Dave",
        "office": "New York"
      }
    ]
  }
]
```

#### GUNZIP

```
GUNZIP(compressed_file_data)
```

Decompresses from a gzip. If there are multiple files in the archive the results are concatenated. Total size of decompressed files must be under 100 MB.

**Categories:** Data Parsing/Conversion

#### GZIP

```
GZIP(file_contents)
```

Compresses a single file with gzip

**Categories:** Data Parsing/Conversion

#### HCL_PARSE

```
HCL_PARSE(hcl_text)
```

Parses Hashicorp Configuration Language (HCL) text

**Categories:** Data Parsing/Conversion

#### HEX_PARSE

```
HEX_PARSE(hex_text, [error_on_invalid_hex: FALSE])
```

Converts hexadecimal text into the text or data it represents

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "text_hex": "546869732069732061207069656365206F662074657874"
  }
}
```

Formula:

```
HEX_PARSE(my_action.text_hex)
```

Output:

```json
"This is a piece of text"
```

### Example 2

Input:

```json
{
  "my_action": {
    "binary_hex": "d21a261f4693f566e5bd71ebae94e5b2"
  }
}
```

Formula:

```
HEX_PARSE(my_action.binary_hex)
```

Output:

```json
"(binary data which can't be represented as text)"
```

### Example 3

Input:

```json
{
  "my_action": {
    "text_hex": "xyz fedcba9876543210"
  }
}
```

Formula:

```
HEX_PARSE(my_action.text_hex, error_on_invalid_hex: TRUE)
```

Output:

```json
"Invalid argument to HEX_PARSE, expected hexadecimal string got xyz fedcba9876543210"
```

#### HMAC_SHA1

```
HMAC_SHA1(text, secret)
```

Converts text into a SHA-1 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
HMAC_SHA1(my_action.message, CREDENTIAL.hmac_secret_key)
```

Output:

```json
"a6e79b05608887928dee13fd841b1bf25c1869fe"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "HASHING",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "md5": "=MD5(my_action.string)",
          "sha256": "=SHA256(my_action.string)",
          "sha256_base64": "=SHA256_BASE64(my_action.string)",
          "sha256_and_base64": "=SHA256(my.string) |> BASE64_ENCODE(%)",
          "HMAC": "=HMAC_SHA256(my_action.string, 'secret')"
        }
      },
      "position": {
        "x": -6615,
        "y": -1470
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": "Apples are the best fruit"
        }
      },
      "position": {
        "x": -6615,
        "y": -1590
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### HMAC_SHA1_BASE64

```
HMAC_SHA1_BASE64(text, secret)
```

Converts text into a Base64 encoded SHA-1 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
HMAC_SHA1_BASE64(my_action.message, CREDENTIAL.hmac_secret_key)
```

Output:

```json
"puebBWCIh5KN7hP9hBsb8lwYaf4="
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 30,
        "y": 2835
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "HMAC_SHA1_BASE64",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "hmac_sha1_base64": "=HMAC_SHA1_BASE64(my_action.message, CREDENTIAL.hmac_secret_key)"
        }
      },
      "position": {
        "x": 30,
        "y": 2925
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### HMAC_SHA256

```
HMAC_SHA256(text, secret)
```

Converts text into a SHA-256 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
HMAC_SHA256(my_action.message, CREDENTIAL.hmac_secret_key)
```

Output:

```json
"5979600ab547894b618706b1c28de6b6d4144c6cafc1286451aa04b7b83abe89"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 300,
        "y": 2835
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "HMAC_SHA256",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "hmac_sha256": "=HMAC_SHA256(my_action.message, CREDENTIAL.hmac_secret_key)"
        }
      },
      "position": {
        "x": 300,
        "y": 2925
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### HMAC_SHA256_BASE64

```
HMAC_SHA256_BASE64(text, secret)
```

Converts text into a Base64 encoded SHA-256 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
HMAC_SHA256_BASE64(my_action.message, CREDENTIAL.hmac_secret_key)
```

Output:

```json
"WXlgCrVHiUthhwaxwo3mttQUTGyvwShkUaoEt7g6vok="
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 540,
        "y": 2835
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "HMAC_SHA256_BASE64",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "hmac_sha256_base64": "=HMAC_SHA256_BASE64(my_action.message, CREDENTIAL.hmac_secret_key)"
        }
      },
      "position": {
        "x": 540,
        "y": 2925
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### HOUR

```
HOUR(date)
```

Returns the hour of the day for the specified date. Accepted Formats: T17:49:01+0000, 17:49:01, or 2022-03-19T17:49:01+0000

**Categories:** Dates/Times

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "2022-03-19T17:49:01+0000"
  }
}
```

Formula:

```
HOUR(my_action.message)
```

Output:

```json
"17"
```

## Sample actions

```json
{
  "standardLibVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "2022-03-19T17:49:01+0000"
        }
      },
      "position": {
        "x": 1035,
        "y": 1080
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "HOUR",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "hour": "=HOUR(my_action.message)"
        }
      },
      "position": {
        "x": 1035,
        "y": 1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### HTML_DECODE

```
HTML_DECODE(text)
```

Converts escaped characters in text to HTML syntax characters, e.g. `&lt;élan&gt;` becomes `<élan>`.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "&lt;h1&gt;Hello World!&lt;/h1&gt;"
  }
}
```

Formula:

```
HTML_DECODE(my_action.message)
```

Output:

```json
"<h1>Hello World!</h1>"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "<h1>Hello World!</h1>"
        }
      },
      "position": {
        "x": 45,
        "y": 1710
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "HTML_DECODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "html_decode": "=HTML_DECODE(my_action.message)"
        }
      },
      "position": {
        "x": 45,
        "y": 1785
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### HTML_ENCODE

```
HTML_ENCODE(text)
```

Escapes HTML syntax characters in text, e.g. `<élan>` becomes `&lt;élan&gt;`.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "<h1>Hello World!</h1>"
  }
}
```

Formula:

```
HTML_ENCODE(my_action.message)
```

Output:

```json
"&lt;h1&gt;Hello World!&lt;/h1&gt;"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "<h1>Hello World!</h1>"
        }
      },
      "position": {
        "x": -180,
        "y": 1710
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "HTML_ENCODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "html_encode": "=HTML_ENCODE(my_action.message)"
        }
      },
      "position": {
        "x": -180,
        "y": 1785
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### IF

```
IF(condition, value_if_true, [value_if_false])
```

If `condition` is `TRUE` returns second argument, if `FALSE` returns `null` or the third argument.

**Categories:** Logic

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
IF(my_action.value>5, "true")
```

Output:

```json
null
```

### Example 2

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
IF(my_action.value>5, "true", "false")
```

Output:

```json
false
```

### Example 3

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
IF(my_action.value>1, "true", "false")
```

Output:

```json
true
```

### Example 4

Input:

```json
{
  "my_action": {
    "value1": 5,
    "value2": 50
  }
}
```

Formula:

```
IF(AND(my_action.value1<my_action.value2, my_action.value<100), my_action.value1, "Value is not within the range")
```

Output:

```json
5
```

### Example 5

Input:

```json
{
  "my_action": {
    "value1": 500,
    "value2": 50
  }
}
```

Formula:

```
IF(AND(my_action.value1<my_action.value2, my_action.value<100), my_action.value1, "Value is not within the range")
```

Output:

```json
"Value is not within the range"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "IF",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "if": "=IF(my_action.num = 3, my_action.fruit, my_action.veg)",
          "less": "=IF(my_action.num < 3, my_action.fruit)",
          "greater": "=IF(my_action.num > 3, my_action.fruit)",
          "and": "=IF(AND(my_action.num > 1, my_action.num <5 ), my_action.fruit)",
          "or": "=IF(OR(my_action.num = 2, my_action.num =3 ), my_action.fruit)"
        }
      },
      "position": {
        "x": -420,
        "y": -5805
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "num": 2,
          "fruit": "apple",
          "veg": "carrot"
        }
      },
      "position": {
        "x": -420,
        "y": -5895
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### IF_ERROR

```
IF_ERROR(expression, fallback_expression)
```

If `expression` produces an error, the result of `fallback_expression` is returned. If there is no error, the result of `expression` is returned.

**Categories:** Logic

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
IF_ERROR(my_action.value, "error!")
```

Output:

```json
5
```

### Example 2

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
IF_ERROR(my_action.value / 0, "error!")
```

Output:

```json
"error!"
```

#### INCLUDES

```
INCLUDES(target, value)
```

Returns `TRUE` if target (which can be text, an object or an array) includes value, otherwise returns `FALSE`. See examples for what "includes" means for different types.

**Categories:** Text, Objects, Arrays

## Examples

### Example 1: If the target is text, returns true if the value is a segment of the text.

Formula:

```
INCLUDES("Hello World", "World")
```

Output:

```json
true
```

### Example 2: Is case-sensitive. Use DOWNCASE() or UPCASE() to make it case-insensitive.

Formula:

```
INCLUDES("Hello World", "world")
```

Output:

```json
false
```

### Example 3: If the target is an array, returns true if the value exactly matches an item in the array.

Formula:

```
INCLUDES(["apple", "banana", "pear"], "banana")
```

Output:

```json
true
```

### Example 4: If the target is an array, returns false if the value does not exactly match an item in the array.

Formula:

```
INCLUDES(["apple", "banana", "pear"], "pea")
```

Output:

```json
false
```

### Example 5: Arrays don't need to contain text.

Formula:

```
INCLUDES([1, 2, 3], 3)
```

Output:

```json
true
```

### Example 6: If the target is an object, returns true if the value exactly matches a key in the object.

Formula:

```
INCLUDES({"name": "Eoin", "age": 30}, "age")
```

Output:

```json
true
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "INCLUDES",
      "description": "Includes checks if a string, array, or an object contain a specific value (in this case fruit).",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "includes": "=INCLUDES(my_action.string, 'Apples')",
          "includes_array": "=INCLUDES(my_action.array, 'bananas')",
          "includes_object": "=INCLUDES(my_action.object, 'fruit')"
        }
      },
      "position": {
        "x": -6570,
        "y": -1125
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": "Apples are the best fruit",
          "array": [
            "apples",
            "bananas",
            "grapes"
          ],
          "object": {
            "fruit": "apple"
          }
        }
      },
      "position": {
        "x": -6570,
        "y": -1215
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### INDEX_OF

```
INDEX_OF(string_or_array, object | LAMBDA(value, expr))
```

Returns the index of the specified term within the provided string or array. With arrays you can provide a LAMBDA as the second argument, and the index of the first matching result it returned.

**Categories:** Arrays, Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "data": [
      "foo",
      "bar"
    ]
  }
}
```

Formula:

```
INDEX_OF(my_action.data, "foo")
```

Output:

```json
0
```

### Example 2

Input:

```json
{
  "my_action": {
    "string": "foobar"
  }
}
```

Formula:

```
INDEX_OF(my_action.string, "bar")
```

Output:

```json
3
```

### Example 3

Input:

```json
{
  "my_action": {
    "data": [
      "foo",
      "bar"
    ]
  }
}
```

Formula:

```
INDEX_OF(my_action.data, "cat")
```

Output:

```json
null
```

### Example 4: Use LAMBDA with arrays to return the index of the first matching value

Input:

```json
{
  "my_action": {
    "data": [
      1,
      2,
      3,
      4,
      5
    ]
  }
}
```

Formula:

```
INDEX_OF(my_action.data, LAMBDA(value, value > 2))
```

Output:

```json
2
```

#### INSERT

```
INSERT(array, index, item)
```

Inserts an item into an array at a given index.

**Categories:** Arrays

## Examples

### Example 1

Formula:

```
INSERT(["a", "b", "c"], 1, "x")
```

Output:

```json
[
  "a",
  "x",
  "b",
  "c"
]
```

### Example 2: Accepts negative indexes.

Formula:

```
INSERT(["a", "b", "c"], -2, "x")
```

Output:

```json
[
  "a",
  "b",
  "x",
  "c"
]
```

#### INTERSECTION

```
INTERSECTION(array1, array2)
```

Returns the intersection between two arrays.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "array1": [
      1,
      2,
      3
    ],
    "array2": [
      1,
      2
    ]
  }
}
```

Formula:

```
INTERSECTION(my_action.array1, my_action.array2)
```

Output:

```json
[
  1,
  2
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array1": [
            1,
            2,
            3
          ],
          "array2": [
            1,
            2
          ]
        }
      },
      "position": {
        "x": 1635,
        "y": 2355
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "INTERSECTION",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value": "=INTERSECTION(my_action.array1, my_action.array2)"
        }
      },
      "position": {
        "x": 1635,
        "y": 2430
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### IN_CIDR

```
IN_CIDR(ip_address, cidr_range)
```

Checks if an IP address or a list of IP addresses are in a given CIDR block.

**Categories:** IP Addresses

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "127.0.0.1"
  }
}
```

Formula:

```
IN_CIDR(my_action.message, "10.0.0.0/8")
```

Output:

```json
false
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "10.162.39.1"
  }
}
```

Formula:

```
IN_CIDR(my_action.message, "10.0.0.0/8")
```

Output:

```json
true
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": [
      "10.162.39.1",
      "10.162.39.2"
    ]
  }
}
```

Formula:

```
IN_CIDR(my_action.message, "10.0.0.0/8")
```

Output:

```json
true
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "127.0.0.1"
        }
      },
      "position": {
        "x": 2085,
        "y": 2625
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "IN_CIDR",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "in_cidr": "=IN_CIDR(my_action.message, \"10.0.0.0/8\")"
        }
      },
      "position": {
        "x": 2160,
        "y": 2730
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "10.162.39.1"
        }
      },
      "position": {
        "x": 2265,
        "y": 2625
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### IS_BLANK

```
IS_BLANK(any)
```

Returns true if a value is falsy ie. `FALSE`, `""`, `[]`, `{}`, `NULL`, and false otherwise. [`IS_PRESENT`](https://www.tines.com/docs/formulas/functions/is-blank) is the opposite of this. See also [`IS_EMPTY`](https://www.tines.com/docs/formulas/functions/is-empty).

**Categories:** Text, Objects, Arrays, Logic, Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value1": ""
  }
}
```

Formula:

```
IS_BLANK(my_action.value1)
```

Output:

```json
{
  "value1": true
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "value2": false
  }
}
```

Formula:

```
IS_BLANK(my_action.value2)
```

Output:

```json
{
  "value2": true
}
```

### Example 3

Input:

```json
{
  "my_action": {
    "value3": []
  }
}
```

Formula:

```
IS_BLANK(my_action.value3)
```

Output:

```json
{
  "value3": true
}
```

### Example 4

Input:

```json
{
  "my_action": {
    "value4": {}
  }
}
```

Formula:

```
IS_BLANK(my_action.value4)
```

Output:

```json
{
  "value4": true
}
```

### Example 5

Input:

```json
{
  "my_action": {
    "value5": null
  }
}
```

Formula:

```
IS_BLANK(my_action.value5)
```

Output:

```json
{
  "value5": true
}
```

### Example 6

Input:

```json
{
  "my_action": {
    "value6": "Hello World"
  }
}
```

Formula:

```
IS_BLANK(my_action.value6)
```

Output:

```json
{
  "value6": false
}
```

### Example 7

Input:

```json
{
  "my_action": {
    "value7": 1
  }
}
```

Formula:

```
IS_BLANK(my_action.value7)
```

Output:

```json
{
  "value7": false
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "",
          "value2": false,
          "value3": [],
          "value4": {},
          "value5": null,
          "value6": "Hello World",
          "value7": 1
        }
      },
      "position": {
        "x": 165,
        "y": 2205
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "IS_BLANK",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "=IS_BLANK(my_action.value1)",
          "value2": "=IS_BLANK(my_action.value2)",
          "value3": "=IS_BLANK(my_action.value3)",
          "value4": "=IS_BLANK(my_action.value4)",
          "value5": "=IS_BLANK(my_action.value5)",
          "value6": "=IS_BLANK(my_action.value6)",
          "value7": "=IS_BLANK(my_action.value7)"
        }
      },
      "position": {
        "x": 165,
        "y": 2295
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### IS_EMAIL

```
IS_EMAIL(text)
```

Checks if a text value follows the email standard. This does not guarantee that it's a valid email.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "john@smith.com"
  }
}
```

Formula:

```
IS_EMAIL(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "john"
  }
}
```

Formula:

```
IS_EMAIL(my_action.message)
```

Output:

```json
false
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": "john@smith"
  }
}
```

Formula:

```
IS_EMAIL(my_action.message)
```

Output:

```json
true
```

### Example 4

Input:

```json
{
  "my_action": {
    "message": "john@smith"
  }
}
```

Formula:

```
IS_EMAIL(my_action.message, strict_tld: TRUE)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "45",
  "actionRuntimeVersion": "6",
  "agents": [
    {
      "disabled": false,
      "name": "IS_EMAIL",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "is_email": "=IS_EMAIL(\"john@smith.com\")"
        }
      },
      "position": {
        "x": 0,
        "y": 390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### IS_EMPTY

```
IS_EMPTY(text | array | object)
```

Returns true if a value is empty ie. `""`, `[]`, `{}` and false otherwise. Target value must be text, array, or an object. See also [`IS_BLANK`](https://www.tines.com/docs/formulas/functions/is-blank).

**Categories:** Text, Objects, Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value1": ""
  }
}
```

Formula:

```
IS_EMPTY(my_action.value1)
```

Output:

```json
{
  "value1": true
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "value2": []
  }
}
```

Formula:

```
IS_EMPTY(my_action.value2)
```

Output:

```json
{
  "value2": true
}
```

### Example 3

Input:

```json
{
  "my_action": {
    "value3": {}
  }
}
```

Formula:

```
IS_EMPTY(my_action.value3)
```

Output:

```json
{
  "value3": true
}
```

### Example 4

Input:

```json
{
  "my_action": {
    "value4": "Hello World"
  }
}
```

Formula:

```
IS_EMPTY(my_action.value4)
```

Output:

```json
{
  "value4": false
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "",
          "value2": [],
          "value3": {},
          "value4": "Hello World"
        }
      },
      "position": {
        "x": 420,
        "y": 2205
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "IS_EMPTY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "=IS_EMPTY(my_action.value1)",
          "value2": "=IS_EMPTY(my_action.value2)",
          "value3": "=IS_EMPTY(my_action.value3)",
          "value4": "=IS_EMPTY(my_action.value4)"
        }
      },
      "position": {
        "x": 420,
        "y": 2295
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### IS_IPV4

```
IS_IPV4(text)
```

Checks if a text value is a valid IPV4 address.

**Categories:** IP Addresses, Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "192.168.0.1"
  }
}
```

Formula:

```
IS_IPV4(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
  }
}
```

Formula:

```
IS_IPV4(my_action.message)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "45",
  "actionRuntimeVersion": "6",
  "agents": [
    {
      "disabled": false,
      "name": "IS_IPV4",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "IS_IPV4": "=IS_IPV4(\"192.168.1.1\")"
        }
      },
      "position": {
        "x": 0,
        "y": 390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "self-hosted:12c8bb2053e50b3ca4b4f45d1a3b7563:826eff0aed4e378159e8ef888a863060"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### IS_IPV6

```
IS_IPV6(text)
```

Checks if a text value is a valid IPV6 address.

**Categories:** IP Addresses, Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
  }
}
```

Formula:

```
IS_IPV6(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "192.168.0.1"
  }
}
```

Formula:

```
IS_IPV6(my_action.message)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "45",
  "actionRuntimeVersion": "6",
  "agents": [
    {
      "disabled": false,
      "name": "IS_IPV6",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "IS_IPV6": "=IS_IPV6(\"2001:0db8:85a3:0000:0000:8a2e:0370:7334\")"
        }
      },
      "position": {
        "x": 0,
        "y": 390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "self-hosted:12c8bb2053e50b3ca4b4f45d1a3b7563:826eff0aed4e378159e8ef888a863060"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### IS_IP_ADDRESS

```
IS_IP_ADDRESS(text)
```

Checks if a text value is a valid IP address

**Categories:** IP Addresses

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "127.0.0.1"
  }
}
```

Formula:

```
IS_IP_ADDRESS(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "hello"
  }
}
```

Formula:

```
IS_IP_ADDRESS(my_action.message)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "127.0.0.1"
        }
      },
      "position": {
        "x": 2460,
        "y": 2625
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "IS_IP_ADDRESS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "is_ip_address": "=IS_IP_ADDRESS(my_action.message)"
        }
      },
      "position": {
        "x": 2535,
        "y": 2730
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "hello"
        }
      },
      "position": {
        "x": 2640,
        "y": 2625
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### IS_JSON

```
IS_JSON(text)
```

Checks if a text value is valid JSON.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "{\"foo\": \"bar\"}"
  }
}
```

Formula:

```
IS_JSON(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "{\"foo\":"
  }
}
```

Formula:

```
IS_JSON(my_action.message)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "45",
  "actionRuntimeVersion": "6",
  "agents": [
    {
      "disabled": false,
      "name": "IS_JSON",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "IS_JSON": "=IS_JSON(\"{\\\"foo\\\": \\\"bar\\\"}\")"
        }
      },
      "position": {
        "x": 0,
        "y": 390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "self-hosted:12c8bb2053e50b3ca4b4f45d1a3b7563:826eff0aed4e378159e8ef888a863060"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### IS_PRESENT

```
IS_PRESENT(any)
```

Returns true unless [blank](https://www.tines.com/docs/formulas/functions/is-blank). The result is false for any of `FALSE`, `""`, `[]`, `{}`, `NULL`, and true otherwise

**Categories:** Text, Objects, Arrays, Logic, Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value1": ""
  }
}
```

Formula:

```
IS_PRESENT(my_action.value1)
```

Output:

```json
{
  "value1": false
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "value2": false
  }
}
```

Formula:

```
IS_PRESENT(my_action.value2)
```

Output:

```json
{
  "value2": false
}
```

### Example 3

Input:

```json
{
  "my_action": {
    "value3": []
  }
}
```

Formula:

```
IS_PRESENT(my_action.value3)
```

Output:

```json
{
  "value3": false
}
```

### Example 4

Input:

```json
{
  "my_action": {
    "value4": {}
  }
}
```

Formula:

```
IS_PRESENT(my_action.value4)
```

Output:

```json
{
  "value4": false
}
```

### Example 5

Input:

```json
{
  "my_action": {
    "value5": null
  }
}
```

Formula:

```
IS_PRESENT(my_action.value5)
```

Output:

```json
{
  "value5": false
}
```

### Example 6

Input:

```json
{
  "my_action": {
    "value6": "Hello World"
  }
}
```

Formula:

```
IS_PRESENT(my_action.value6)
```

Output:

```json
{
  "value6": true
}
```

### Example 7

Input:

```json
{
  "my_action": {
    "value7": 1
  }
}
```

Formula:

```
IS_PRESENT(my_action.value7)
```

Output:

```json
{
  "value7": true
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "",
          "value2": false,
          "value3": [],
          "value4": {},
          "value5": null,
          "value6": "Hello World",
          "value7": 1
        }
      },
      "position": {
        "x": 165,
        "y": 2205
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "IS_PRESENT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value1": "=IS_PRESENT(my_action.value1)",
          "value2": "=IS_PRESENT(my_action.value2)",
          "value3": "=IS_PRESENT(my_action.value3)",
          "value4": "=IS_PRESENT(my_action.value4)",
          "value5": "=IS_PRESENT(my_action.value5)",
          "value6": "=IS_PRESENT(my_action.value6)",
          "value7": "=IS_PRESENT(my_action.value7)"
        }
      },
      "position": {
        "x": 165,
        "y": 2295
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### IS_URL

```
IS_URL(text)
```

Checks if a text value follows the URL standard. This does not guarantee that it's a valid URL.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "https://example.com"
  }
}
```

Formula:

```
IS_URL(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "example.com"
  }
}
```

Formula:

```
IS_URL(my_action.message)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "45",
  "actionRuntimeVersion": "6",
  "agents": [
    {
      "disabled": false,
      "name": "IS_URL",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "is_url": "=IS_URL(\"https://tines.io\")"
        }
      },
      "position": {
        "x": 0,
        "y": 390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "self-hosted:12c8bb2053e50b3ca4b4f45d1a3b7563:826eff0aed4e378159e8ef888a863060"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### IS_VALID_JSON_SCHEMA

```
IS_VALID_JSON_SCHEMA(object, any)
```

Returns true if the object provided matches the provided JSON schema, using the [JSON Schema syntax](https://json-schema.org/).

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "schema": {
    "type": "object",
    "properties": {
      "abc": {
        "type": "integer",
        "minimum": 11
      }
    }
  },
  "my_action": {
    "value1": {
      "abc": 11
    }
  }
}
```

Formula:

```
IS_VALID_JSON_SCHEMA(schema, my_action.value1)
```

Output:

```json
{
  "value1": true
}
```

#### IS_XML

```
IS_XML(text)
```

Checks if a text value is valid XML.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "<foo></foo>"
  }
}
```

Formula:

```
IS_XML(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "<foo></bar>"
  }
}
```

Formula:

```
IS_XML(my_action.message)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "45",
  "actionRuntimeVersion": "6",
  "agents": [
    {
      "disabled": false,
      "name": "IS_XML",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "IS_XML": "=IS_XML(\"<foo></foo>\")"
        }
      },
      "position": {
        "x": 0,
        "y": 390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "self-hosted:12c8bb2053e50b3ca4b4f45d1a3b7563:826eff0aed4e378159e8ef888a863060"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### IS_YAML

```
IS_YAML(text, [parse_date_or_time: FALSE], [strict: FALSE])
```

Checks if a text value is valid YAML. When `strict` is `TRUE`, the function will only return true if the input is a valid YAML document that contains key-value pairs.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "foo: bar"
  }
}
```

Formula:

```
IS_YAML(my_action.message)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "foo: bar- foo:"
  }
}
```

Formula:

```
IS_YAML(my_action.message)
```

Output:

```json
false
```

### Example 3

Input:

```json
{
  "my_action": "\nLinkedDate: 2007-04-01 12:00:00\n"
}
```

Formula:

```
IS_YAML(my_action, parse_date_or_time: TRUE)
```

Output:

```json
true
```

### Example 4

Input:

```json
{
  "my_action": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<note>\n    <body>foo bar</body>\n</note>"
}
```

Formula:

```
IS_YAML(my_action, strict: TRUE)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "45",
  "actionRuntimeVersion": "6",
  "agents": [
    {
      "disabled": false,
      "name": "IS_YAML",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "IS_YAML": "=IS_YAML(\"foo: bar\")"
        }
      },
      "position": {
        "x": 0,
        "y": 390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "self-hosted:12c8bb2053e50b3ca4b4f45d1a3b7563:826eff0aed4e378159e8ef888a863060"
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### JOIN

```
JOIN(array, join_text)
```

Combines the elements in an array into a single text value using the argument as a separator.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
JOIN(my_action.message, " and ")
```

Output:

```json
"north and south and east and west"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "flat_array": [
            "apple",
            "pear",
            "orange",
            "grapes"
          ]
        }
      },
      "position": {
        "x": -1020,
        "y": -6465
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "JOIN",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "join": "=JOIN(my_action.flat_array, ', ')"
        }
      },
      "position": {
        "x": -1020,
        "y": -6330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### JSONPATH

```
JSONPATH(object, path)
```

Evaluates objects using JSONPath expressions. Supports escaped and non-escaped JSON. JSONPath expressions support wildcards, filters, and slices. See [JSONPath](https://goessner.net/articles/JsonPath/) or the [Online Evaluator](https://jsonpath.com/) for more information. Please note: Tines uses [JSONPath](https://goessner.net/articles/JsonPath/) version 0.9.9, which may not support all features available in modern online JSONPath evaluators. More examples of how to use JSONPATH can be seen in our [Story Library](https://www.tines.com/story-library/87758/navigate-complex-data-structures-with-tines-jsonpath-function)

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "json": {
      "phoneNumbers": [
        {
          "name": "John",
          "type": "iPhone",
          "country": "Australia"
        },
        {
          "name": "Joe",
          "type": "Android",
          "country": "Australia"
        },
        {
          "name": "Jess",
          "type": "home",
          "country": "Ireland"
        }
      ]
    }
  }
}
```

Formula:

```
JSONPATH(my_action.json, "$.phoneNumbers[*].type")
```

Output:

```json
[
  "iPhone",
  "Android",
  "home"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": {
            "phoneNumbers": [
              {
                "name": "John",
                "type": "iPhone",
                "country": "Australia"
              },
              {
                "name": "Joe",
                "type": "Android",
                "country": "Australia"
              },
              {
                "name": "Jess",
                "type": "home",
                "country": "Ireland"
              }
            ]
          }
        }
      },
      "position": {
        "x": 1965,
        "y": 2895
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "JSONPATH",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "jsonpath": "=JSONPATH(my_action, \"$.message.phoneNumbers[*].type\")"
        }
      },
      "position": {
        "x": 1815,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "JSONPATH",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "jsonpath": "=JSONPATH(my_action, \"$.message.phoneNumbers[*].name\")"
        }
      },
      "position": {
        "x": 1965,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "JSONPATH",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "jsonpath": "=JSONPATH(my_action,\"$..phoneNumbers[?(@.type=='home')].country\")"
        }
      },
      "position": {
        "x": 2115,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 3
    }
  ],
  "diagramNotes": []
}
```

#### JSON_PARSE

```
JSON_PARSE(text)
```

Parses escaped JSON text. If used in a [formula field](https://www.tines.com/docs/formulas#formula-pills-in-text-fields-vs-formula-fields), it will return a JSON object; if used inside a text field it will return unescaped JSON text.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "json": "{\"payload\":{\"key\":\"value\",\"array_example\":[\"foo\",\"bar\"],\"object_example\":{\"key\":\"value\"}}}"
  }
}
```

Formula:

```
JSON_PARSE(my_action.json)
```

Output:

```json
{
  "payload": {
    "array_example": [
      "foo",
      "bar"
    ],
    "key": "value",
    "object_example": {
      "key": "value"
    }
  }
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "json": "{\"payload\":{\"key\":\"value\",\"array_example\":[\"foo\",\"bar\"],\"object_example\":{\"key\":\"value\"}}}"
        }
      },
      "position": {
        "x": 1635,
        "y": 2895
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "JSON_PARSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "json_parse": "=JSON_PARSE(my_action.json)"
        }
      },
      "position": {
        "x": 1635,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### JSON_SCHEMA_VALIDATE

```
JSON_SCHEMA_VALIDATE(object, any)
```

Returns a validation error message if the object provided does not match the provided json schema, using the https://json-schema.org/ syntax or an empty string otherwise.

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "schema": {
    "type": "object",
    "properties": {
      "abc": {
        "type": "integer",
        "minimum": 11
      }
    }
  },
  "my_action": {
    "value1": {
      "abc": 10
    }
  }
}
```

Formula:

```
JSON_SCHEMA_VALIDATE(schema, my_action.value1)
```

Output:

```json
"Number at `/abc` is less than: 11"
```

#### JWK_TO_PEM

```
JWK_TO_PEM(jwk, [include_private: FALSE])
```

Converts a JSON Web Key (JWK) to PEM format. By default returns the public key in PEM format. When `include_private` is `TRUE`, returns the private key in PEM format if available. Only supports RSA and ECDSA keys.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "jwk": {
      "kty": "RSA",
      "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISO_BIZ1tOl6U7QElTqLaXyJjnfwJTQwKPUwD3xQGNzqXPv2VZKtlkPKhEjRXN9YXJnS9QsQFRU5L2rN2tOXJm4a-BsxoYPAmCl1h",
      "e": "AQAB"
    }
  }
}
```

Formula:

```
JWK_TO_PEM(my_action.jwk)
```

Output:

```json
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0vx7agoebGcQSuuPiLJX\nZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tS\noc/BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt\n7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0\nzgdAZHzu6qMQvRL5hajrn1n91CbOpbISO_BIZ1tOl6U7QElTqLaXyJjnfwJTQwKP\nUwD3xQGNzqXPv2VZKtlkPKhEjRXN9YXJnS9QsQFRU5L2rN2tOXJm4a-BsxoYPAmC\nl1h\nwIDAQAB\n-----END PUBLIC KEY-----\n"
```

### Example 2

Input:

```json
{
  "my_action": {
    "jwk": {
      "kty": "RSA",
      "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISO_BIZ1tOl6U7QElTqLaXyJjnfwJTQwKPUwD3xQGNzqXPv2VZKtlkPKhEjRXN9YXJnS9QsQFRU5L2rN2tOXJm4a-BsxoYPAmCl1h",
      "e": "AQAB",
      "d": "X4cTI5uEHYj85CiGCJOg_E7Dh0t2zp2Px3KT-H-dN1nzCXJKt9-IQKJQ0h7RQxd1"
    }
  }
}
```

Formula:

```
JWK_TO_PEM(my_action.jwk, include_private: TRUE)
```

Output:

```json
"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDS_Htqih5sZxBK\n64-IslZmm032ed2tCZtcSmzZqIAVtaEzvwuFbHhxtt8AC1VPzrPC7VErtq8UXG6E\nNHUvq1Khz8EkQI95tYpFeMFkKIVXifeSSeeEyy2fri1n_Zb7kmwZjgdzmf3IFcCv\nCX3eWq3v9E3nDoJ_SHhDJDm_7rlgaNBHT8UNbZC_OpjfrxBAyJwC1pKrOzwolmCd\nhv1zt3TOB0BkfO7qoxC9EvmFqOufWf3UJs6lshI78EhnW06XpTtASVOotpfImOd_\nAlNDAo9TAPfFAY3Opc-_ZVkq2WQ8qESNFc31hcmdL1CxAVFTkvas3a05cmbhr4Gz\nGhg8CYKXWHAgMBAAECggEAX4cTI5uEHYj85CiGCJOg_E7Dh0t2zp2Px3KT-H-dN1n\nzCXJKt9-IQKJQ0h7RQxd1AgMBAAECggEAX4cTI5uEHYj85CiGCJOg_E7Dh0t2zp2P\nx3KT-H-dN1nzCXJKt9-IQKJQ0h7RQxd1AgMBAAECggEAX4cTI5uEHYj85CiGCJOg_\nE7Dh0t2zp2Px3KT-H-dN1nzCXJKt9-IQKJQ0h7RQxd1\n-----END PRIVATE KEY-----\n"
```

#### JWT_DECODE

```
JWT_DECODE(token, secret_or_key, algorithm, **options)
```

Decodes and optionally verifies a JWT token using the provided secret/key and algorithm. Returns an object containing the payload, header, and verification status.

Supports HMAC (HS256/HS384/HS512), RSA (RS256/RS384/RS512), and ECDSA (ES256/ES384/ES512) algorithms. Can validate standard JWT claims like exp, nbf, iss, aud, jti, sub, and custom required claims. Supports JWK (JSON Web Key) and JWKS (JSON Web Key Set) formats. 

 The options should be provided in the same manner as the options for the [JWT.decode](https://github.com/jwt/ruby-jwt?tab=readme-ov-file#algorithms-and-usage) function (e.g., `verify`, `jwks`, `verify_expiration`, `verify_not_before`, `leeway`, `iss`/`verify_iss`, `aud`/`verify_aud`, `sub`/`verify_sub`, `jti`/`verify_jti`, `required_claims`). For example, to verify the iss claim: `iss: "my-app", verify_iss: TRUE`

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.FoW4dUPr9HWzOI8S7Ohpe3hGULZEJhNJeouOX8f1sz8"
}
```

Formula:

```
JWT_DECODE(token, "secret", "HS256")
```

Output:

```json
{
  "payload": {
    "user_id": 123
  },
  "header": {
    "alg": "HS256"
  },
  "verified": true
}
```

### Example 2

Input:

```json
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.invalid_signature"
}
```

Formula:

```
JWT_DECODE(token, "secret", "HS256", verify: FALSE)
```

Output:

```json
{
  "payload": {
    "user_id": 123
  },
  "header": {
    "typ": "JWT",
    "alg": "HS256"
  },
  "verified": false
}
```

### Example 3

Input:

```json
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJteWFwcCIsInVzZXJfaWQiOjEyM30.signature",
  "rsa_public_key": "-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----"
}
```

Formula:

```
JWT_DECODE(token, rsa_public_key, "RS256", iss: "myapp", verify_iss: TRUE)
```

Output:

```json
{
  "payload": {
    "iss": "myapp",
    "user_id": 123
  },
  "header": {
    "typ": "JWT",
    "alg": "RS256"
  },
  "verified": true
}
```

### Example 4

Input:

```json
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.signature",
  "jwk_key": {
    "kty": "RSA",
    "n": "base64url_encoded_modulus",
    "e": "AQAB"
  }
}
```

Formula:

```
JWT_DECODE(token, jwk_key, "RS256")
```

Output:

```json
{
  "payload": {
    "user_id": 123
  },
  "header": {
    "typ": "JWT",
    "alg": "RS256"
  },
  "verified": true
}
```

### Example 5

Input:

```json
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtleS0xIn0.eyJ1c2VyX2lkIjoxMjN9.signature",
  "jwks_set": {
    "keys": [
      {
        "kty": "RSA",
        "kid": "key-1",
        "use": "sig",
        "alg": "RS256",
        "n": "base64url_encoded_modulus",
        "e": "AQAB"
      }
    ]
  }
}
```

Formula:

```
JWT_DECODE(token, NULL, "RS256", jwks: jwks_set)
```

Output:

```json
{
  "payload": {
    "user_id": 123
  },
  "header": {
    "typ": "JWT",
    "alg": "RS256",
    "kid": "key-1"
  },
  "verified": true
}
```

#### JWT_SIGN

```
JWT_SIGN(claim_set, key, [algorithm=RS256], [headers])
```

Creates a JSON Web Token from the provided payload, secret/key, and algorithm. Default algorithm is RS256 if not specified. Supports HMAC (HS256/HS384/HS512), RSA (RS256/RS384/RS512), and ECDSA (ES256/ES384/ES512) algorithms. Can include standard JWT claims like exp, nbf, iss, aud, jti, iat, sub and custom header fields like kid, typ. Supports both PEM format keys and JWK (JSON Web Key) format for RSA and ECDSA keys.

**Categories:** Hashing/Signing

## Examples

### Example 1

Formula:

```
JWT_SIGN({"iss":"Test Company","iat":1676003525,"exp":1707539525,"aud":"www.example.com","sub":"jsmith@example.com"}, CREDENTIAL.jwt_hmac_key, "HS256", {"typ":"JWT"})
```

### Example 2

Input:

```json
{}
```

Formula:

```
JWT_SIGN({"user_id": 123}, "secret", "HS256")
```

Output:

```json
"eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.FoW4dUPr9HWzOI8S7Ohpe3hGULZEJhNJeouOX8f1sz8"
```

### Example 3

Input:

```json
{
  "payload": {
    "user_id": 123,
    "role": "admin"
  },
  "rsa_private_key": "-----BEGIN RSA PRIVATE KEY-----\\n...\\n-----END RSA PRIVATE KEY-----"
}
```

Formula:

```
JWT_SIGN(payload, rsa_private_key, "RS256", exp: DATE(NOW() + DAYS(1), "%s"))
```

Output:

```json
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcwNTg4NjQwMH0.signature"
```

### Example 4

Input:

```json
{
  "payload": {
    "sub": "user123"
  },
  "jwk_key": {
    "kty": "RSA",
    "n": "base64url_encoded_modulus",
    "e": "AQAB",
    "d": "base64url_encoded_private_exponent"
  }
}
```

Formula:

```
JWT_SIGN(payload, jwk_key, "RS256", kid: "key-123")
```

Output:

```json
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtleS0xMjMifQ.eyJzdWIiOiJ1c2VyMTIzIn0.signature"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "claims": {
            "iss": "Test Company",
            "iat": 1676003525,
            "exp": 1707539525,
            "aud": "www.example.com",
            "sub": "jsmith@example.com"
          },
          "headers": {
            "typ": "JWT"
          }
        }
      },
      "position": {
        "x": 2325,
        "y": 2895
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "JWT_SIGN",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "jwt_sign": "=JWT_SIGN(my_action.claims, CREDENTIAL.jwt_hmac_key, \"HS256\", my_action.headers)"
        }
      },
      "position": {
        "x": 2325,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### KEYS

```
KEYS(object)
```

Returns an array of keys present in the object.

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "blue": 2,
    "green": 3,
    "red": 1
  }
}
```

Formula:

```
KEYS(object)
```

Output:

```json
[
  "red",
  "blue",
  "green"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": {
            "red": 1,
            "blue": 2,
            "green": 3
          }
        }
      },
      "position": {
        "x": 2610,
        "y": 2895
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "KEYS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "keys": "=KEYS(my_action.message)"
        }
      },
      "position": {
        "x": 2610,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### LAMBDA

```
LAMBDA([parameter1, parameter2, …,] calculation)
```

Creates a custom, reusable function.

The last argument is the calculation you want to perform, all previous arguments are the parameters for this calculation

**Categories:** Lambdas

## Examples

### Example 1: Select all the elements in an array that regex match the string "r"​

Input:

```json
{
  "my_array": [
    "red",
    "blue",
    "green"
  ]
}
```

Formula:

```
FILTER(input.my_array, LAMBDA(element, MATCH(element, "r")))
```

Output:

```json
[
  "red",
  "green"
]
```

### Example 2: Get the difference between 2 arrays, i.e. get elements from array_two that are not present in array_one​

Input:

```json
{
  "array_one": [
    "dog",
    "cat",
    "turtle",
    "dinosaur",
    "lizard",
    "chicken",
    "koala"
  ],
  "array_two": [
    "cat",
    "elephant",
    "giraffe",
    "penguin",
    "tiger",
    "koala"
  ]
}
```

Formula:

```
FILTER(input.array_two, LAMBDA(array_two_elem, NOT(INCLUDES(input.array_one, array_two_elem))))
```

Output:

```json
[
  "elephant",
  "giraffe",
  "penguin",
  "tiger"
]
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array": [
            "red",
            "blue",
            "green"
          ]
        }
      },
      "position": {
        "x": 2910,
        "y": 2895
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "LAMBDA",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "lambda": "=FILTER(my_action.array, LAMBDA(element, MATCH(element, \"r\")))"
        }
      },
      "position": {
        "x": 2910,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array_one": [
            "dog",
            "cat",
            "turtle",
            "dinosaur",
            "lizard",
            "chicken",
            "koala"
          ],
          "array_two": [
            "cat",
            "elephant",
            "giraffe",
            "penguin",
            "tiger",
            "koala"
          ]
        }
      },
      "position": {
        "x": 3090,
        "y": 2895
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "LAMBDA",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "lambda": "=FILTER(my_action.array_two, LAMBDA(array_two_elem, NOT(INCLUDES(my_action.array_one, array_two_elem))))\n"
        }
      },
      "position": {
        "x": 3090,
        "y": 3000
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### LAST

```
LAST(array)
```

Returns the last element of an array.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
LAST(my_action.message)
```

Output:

```json
"west"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "LAST",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "last": "=LAST(my_action.flat_array)"
        }
      },
      "position": {
        "x": -6555,
        "y": -1290
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "flat_array": [
            "apple",
            "pear",
            "orange",
            "grapes"
          ]
        }
      },
      "position": {
        "x": -6555,
        "y": -1395
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### LAST_INDEX_OF

```
LAST_INDEX_OF(string_or_array, term)
```

Returns the last index of the specified term within the provided string or array. With arrays you provide a LAMBDA as the second argument.

**Categories:** Arrays, Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "data": [
      "foo",
      "bar",
      "foo"
    ]
  }
}
```

Formula:

```
LAST_INDEX_OF(my_action.data, "foo")
```

Output:

```json
2
```

### Example 2

Input:

```json
{
  "my_action": {
    "string": "foo bar foo"
  }
}
```

Formula:

```
LAST_INDEX_OF(my_action.string, "foo")
```

Output:

```json
8
```

### Example 3

Input:

```json
{
  "my_action": {
    "data": [
      "foo",
      "bar"
    ]
  }
}
```

Formula:

```
LAST_INDEX_OF(my_action.data, "cat")
```

Output:

```json
"NULL"
```

### Example 4

Input:

```json
{
  "my_action": {
    "data": [
      "foo",
      "bar"
    ]
  }
}
```

Formula:

```
LAST_INDEX_OF(my_action.data, LAMBDA(a, a = "bar"))
```

Output:

```json
1
```

#### LDIF_PARSE

```
LDIF_PARSE(ldif_file_text)
```

Parse text from a LDAP Data Interchange Format (LDIF) file into an object

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "dn: cn=The Postmaster,dc=example,dc=com\nobjectClass: organizationalRole\ncn: The Postmaster"
  }
}
```

Formula:

```
LDIF_PARSE(my_action.message)
```

Output:

```json
[
  {
    "cn": [
      "The Postmaster"
    ],
    "objectclass": [
      "organizationalRole"
    ]
  }
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "dn: CN=John Smith,OU=Legal,DC=example,DC=com\nchangetype: modify\nreplace: employeeID\nemployeeID: 1234\n-\nreplace: employeeNumber\nemployeeNumber: 98722\n-\nreplace: extensionAttribute6\nextensionAttribute6: JSmith98"
        }
      },
      "position": {
        "x": 60,
        "y": 3390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "LDIF_PARSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "ldif_parse": "=LDIF_PARSE(my_action.message)"
        }
      },
      "position": {
        "x": 60,
        "y": 3480
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### LEGACY_STRINGIFY

```
LEGACY_STRINGIFY(object)
```

Converts an object parameter to a legacy String representation.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "data": {
      "foo": "bar"
    }
  }
}
```

Formula:

```
LEGACY_STRINGIFY(my_action.data)
```

Output:

```json
"{\"foo\"=>\"bar\"}"
```

#### LEVENSHTEIN_DISTANCE

```
LEVENSHTEIN_DISTANCE(string_one, string_two, [insertion_cost: 2], [deletion_cost: 2], [substitution_cost: 1])
```

Compute the Levenshtein distance between two strings.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "name_one": "Smith",
    "name_two": "Simth"
  }
}
```

Formula:

```
LEVENSHTEIN_DISTANCE(my_action.name_one, my_action.name_two)
```

Output:

```json
2
```

### Example 2

Input:

```json
{
  "my_action": {
    "name_one": "kitten",
    "name_two": "sitting"
  }
}
```

Formula:

```
LEVENSHTEIN_DISTANCE(my_action.name_one, my_action.name_two, insertion_cost: 1, deletion_cost: 1)
```

Output:

```json
3
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "name_one": "Smith",
          "name_two": "Simth"
        }
      },
      "position": {
        "x": 1845,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "LEVENSHTEIN_DISTANCE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "levenshtein_distance": "=LEVENSHTEIN_DISTANCE(my_action.name_one, my_action.name_two)"
        }
      },
      "position": {
        "x": 1920,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "name_one": "Smith",
          "name_two": "Jones"
        }
      },
      "position": {
        "x": 1995,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### LSTRIP

```
LSTRIP(text)
```

Removes all whitespace (tabs, spaces, and newlines) from the beginning of text.

Does not affect spaces between words.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "       Hello World!      "
  }
}
```

Formula:

```
LSTRIP(my_action.message)
```

Output:

```json
"Hello World!      "
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "LSTRIP",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "lstrip": "<<LSTRIP(my_action.string)>>"
        }
      },
      "position": {
        "x": -6285,
        "y": -1140
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "If you click before and after \"hello my name is martin\" you'll see extra spaces. LSTRIP removes all whitespace.",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": " hello my name is martin "
        }
      },
      "position": {
        "x": -6285,
        "y": -1260
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### MAP

```
MAP(array_of_objects, path)
```

Creates an array by extracting the values for a given key or path from an array of objects. Path is a dot-separated string of keys. If a property name has spaces, wrap it in square brackets and double quotes.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "animals": [
      {
        "color": "Brown",
        "type": "Dog"
      },
      {
        "color": "Beige",
        "type": "Cat"
      }
    ]
  }
}
```

Formula:

```
MAP(my_action.animals, "type")
```

Output:

```json
[
  "Dog",
  "Cat"
]
```

### Example 2

Input:

```json
{
  "my_action": [
    {
      "a": {
        "b": {
          "c": "d"
        }
      }
    }
  ]
}
```

Formula:

```
MAP(my_action, "a.b.c")
```

Output:

```json
"d"
```

### Example 3

Input:

```json
{
  "fetch_data": {
    "users": [
      {
        "User ID": "ID1"
      },
      {
        "User ID": "ID2"
      }
    ]
  }
}
```

Formula:

```
MAP(fetch_data.users, '["User ID"]')
```

Output:

```json
[
  "ID1",
  "ID2"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "animals": [
            {
              "type": "Dog",
              "color": "Brown"
            },
            {
              "type": "Cat",
              "color": "Beige"
            }
          ]
        }
      },
      "position": {
        "x": -195,
        "y": 2505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MAP",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "map": "=MAP(my_action.animals,\"type\")"
        }
      },
      "position": {
        "x": -195,
        "y": 2595
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MAP_LAMBDA

```
MAP_LAMBDA(array_or_object, lambda)
```

Return an array that is the result of calling lambda with each element of the input array or each key-value pair of the input object.

**Categories:** Lambdas

## Examples

### Example 1: Construct an array of elements from input array.

Input:

```json
{
  "my_action": {
    "email_array": [
      "foo@tines.com",
      "bar@tines.com"
    ]
  }
}
```

Formula:

```
MAP_LAMBDA(my_action.email_array, LAMBDA(elem, OBJECT("email_address", OBJECT("address", elem))))
```

Output:

```json
[
  {
    "email_address": {
      "address": "foo@tines.io"
    }
  },
  {
    "email_address": {
      "address": "bar@tines.io"
    }
  }
]
```

### Example 2: Construct an array of elements from input object.

Input:

```json
{
  "my_action": {
    "my_object": {
      "foo": [
        1,
        2,
        3
      ],
      "baz": [
        4,
        5,
        6
      ]
    }
  }
}
```

Formula:

```
MAP_LAMBDA(my_action.my_object, LAMBDA(key, value, OBJECT("key", key, "sum", SUM(value))))
```

Output:

```json
[
  {
    "key": "foo",
    "sum": 6
  },
  {
    "key": "baz",
    "sum": 15
  }
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "email_array": [
            "foo@tines.com",
            "bar@tines.com"
          ]
        }
      },
      "position": {
        "x": 2235,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MAP_LAMBDA",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "map_lambda": "=MAP_LAMBDA(my_action.email_array, LAMBDA(elem, OBJECT(\"email_address\", OBJECT(\"address\", elem))))\n"
        }
      },
      "position": {
        "x": 2235,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MATCH

```
MATCH(text, regex_to_match,  [modifiers])
```

Returns true if text contains the regex expression regex_to_match and false otherwise. Optionally, include a string containing [Ruby modifiers](https://docs.ruby-lang.org/en/master/Regexp.html#class-Regexp-label-Modes) – e.g. `i` for case insensitive mode.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "The quick brown fox jumps over the lazy dog. It barked."
  }
}
```

Formula:

```
MATCH(my_action.message, "[A-Z]")
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "the quick brown fox jumps over the lazy dog. it barked."
  }
}
```

Formula:

```
MATCH(my_action.message, "[A-Z]")
```

Output:

```json
false
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": "the quick brown fox jumps over the lazy dog. it barked."
  }
}
```

Formula:

```
MATCH(my_action.message, "[A-Z]", "i")
```

Output:

```json
true
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "MATCH",
      "description": "It's searching a string for something that begins with a capital or lower case 'A' and ends with 's' (in this example \"Apples\" matches).",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "Match": "=MATCH(my_action.string, '[Aa].+s')"
        }
      },
      "position": {
        "x": -6645,
        "y": -1215
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": "Apples are the best fruit"
        }
      },
      "position": {
        "x": -6645,
        "y": -1305
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### MAX

```
MAX(array) or MAX(number1, number2, ...)
```

Returns the argument with the highest numeric value. Can either be called with a single argument which must be an array or multiple arguments

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 13
  }
}
```

Formula:

```
MAX(my_action.message, 20)
```

Output:

```json
20
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": [
      1,
      3,
      2
    ]
  }
}
```

Formula:

```
MAX(my_action.message)
```

Output:

```json
3
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 13
        }
      },
      "position": {
        "x": 2505,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MAX",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "max": "=MAX(my_action.message, 20)"
        }
      },
      "position": {
        "x": 2505,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MD5

```
MD5(text)
```

Calculates the hex encoded MD5 hash of some text.

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
MD5(my_action.message)
```

Output:

```json
"802377ace177715b8b24ec6703fb8d6f"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "MD5",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "md5": "=MD5(my_action.string)"
        }
      },
      "position": {
        "x": -6195,
        "y": -1665
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": "Apples are the best fruit"
        }
      },
      "position": {
        "x": -6195,
        "y": -1770
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### MD5_BASE64

```
MD5_BASE64(text)
```

Calculates the Base64 encoded MD5 hash of text.

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
MD5_BASE64(my_action.message)
```

Output:

```json
"gCN3rOF3cVuLJOxnA/uNbw=="
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Hello World"
        }
      },
      "position": {
        "x": 45,
        "y": 3105
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MD5_BASE64",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "md5_base64": "=MD5_BASE64(my_action.message)"
        }
      },
      "position": {
        "x": 45,
        "y": 3195
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MEDIAN

```
MEDIAN(array) or MEDIAN(number1, number2, ...)
```

Returns the median numeric value. Can either be called with a single argument which must be an array or multiple numeric arguments.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      1,
      2,
      3,
      4
    ]
  }
}
```

Formula:

```
MEDIAN(my_action.message)
```

Output:

```json
2.5
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 1
  }
}
```

Formula:

```
MEDIAN(my_action.message, 2, 3)
```

Output:

```json
2
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "60",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            1,
            2
          ]
        }
      },
      "position": {
        "x": 3075,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MEDIAN",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "median": "=MEDIAN(my_action.message)"
        }
      },
      "position": {
        "x": 3075,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "60",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 1
        }
      },
      "position": {
        "x": 3075,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MEDIAN",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "median": "=MEDIAN(my_action.message, 2, 3)"
        }
      },
      "position": {
        "x": 3075,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MERGE

```
MERGE(object, object, ...)
```

Creates a new object by merging two or more objects together.

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "object1": {
      "city": "Melbourne"
    },
    "object2": {
      "country": "Australia"
    }
  }
}
```

Formula:

```
MERGE(my_action.object1,my_action.object2)
```

Output:

```json
{
  "city": "Melbourne",
  "country": "Australia"
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "object1": {
      "country": "Ireland"
    },
    "object2": {
      "country": "Australia"
    }
  }
}
```

Formula:

```
MERGE(my_action.object1,my_action.object2)
```

Output:

```json
{
  "country": "Australia"
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "MERGE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "=MERGE(my_action.obj1, my_action.obj2, my_action.obj3)"
        }
      },
      "position": {
        "x": -6540,
        "y": -1470
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "obj1": {
            "animal": "dog"
          },
          "obj2": {
            "colour": "red"
          },
          "obj3": {
            "fruit": "apple"
          }
        }
      },
      "position": {
        "x": -6540,
        "y": -1590
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### MIME_HEADER_DECODE

```
MIME_HEADER_DECODE(encoded_string, target_encoding = "utf-8")
```

Decodes email header fields encoded in accordance with RFC 2047.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Formula:

```
MIME_HEADER_DECODE(encoded_string, target_encoding = "utf-8")
```

Output:

```json
"Decoded email header field"
```

### Example 2

Formula:

```
MIME_HEADER_DECODE("=?US-ASCII?Q?John_Smith?= <smith / gmail.com>")
```

Output:

```json
"John Smith <smith / gmail.com>"
```

#### MIN

```
MIN(array) or MIN(number1, number2, ...)
```

Returns the argument with the lowest numeric value. Can either be called with a single argument which must be an array or multiple arguments.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 13
  }
}
```

Formula:

```
MIN(my_action.message, 20)
```

Output:

```json
13
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": [
      1,
      3,
      2
    ]
  }
}
```

Formula:

```
MIN(my_action.message)
```

Output:

```json
1
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 13
        }
      },
      "position": {
        "x": 2895,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MIN",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "min": "=MIN(my_action.message, 20)"
        }
      },
      "position": {
        "x": 2895,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            1,
            3,
            2
          ]
        }
      },
      "position": {
        "x": 3075,
        "y": 3225
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MIN",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "min": "=MIN(my_action.message)"
        }
      },
      "position": {
        "x": 3075,
        "y": 3330
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MINUS

```
MINUS(number, number)
```

Subtracts a number from another number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 4
  }
}
```

Formula:

```
MINUS(my_action.message, 2)
```

Output:

```json
2
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 67.28
  }
}
```

Formula:

```
MINUS(my_action.message, 12)
```

Output:

```json
55.28
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 4
        }
      },
      "position": {
        "x": 1830,
        "y": 3525
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MINUS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "minus": "=MINUS(my_action.message, 2)"
        }
      },
      "position": {
        "x": 1830,
        "y": 3630
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 67.28
        }
      },
      "position": {
        "x": 2010,
        "y": 3525
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MINUS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "minus": "=MINUS(my_action.message, 12)"
        }
      },
      "position": {
        "x": 2010,
        "y": 3630
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MINUTE

```
MINUTE(date)
```

Returns the minute of the hour for the specified date. Accepted Formats: T17:49:01+0000, 17:49:01, or 2022-03-19T17:49:01+0000

**Categories:** Dates/Times

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "2022-03-19T17:49:01+0000"
  }
}
```

Formula:

```
MINUTE(my_action.message)
```

Output:

```json
"49"
```

## Sample actions

```json
{
  "standardLibraryVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "2022-03-19T17:49:01+0000"
        }
      },
      "position": {
        "x": 1035,
        "y": 1080
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "MINUTE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "minute": "=MINUTE(my_action.message)"
        }
      },
      "position": {
        "x": 1035,
        "y": 1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MODULO

```
MODULO(numerator, denominator)
```

Returns the remainder of a division operation.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 3
  }
}
```

Formula:

```
MODULO(my_action.message, 2)
```

Output:

```json
1
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 48
  }
}
```

Formula:

```
MODULO(my_action.message, 5)
```

Output:

```json
3
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": 67.28
  }
}
```

Formula:

```
MODULO(my_action.message, 12)
```

Output:

```json
7.28
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 3
        }
      },
      "position": {
        "x": 2220,
        "y": 3540
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MODULO",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "modulo": "=MODULO(my_action.message, 2)"
        }
      },
      "position": {
        "x": 2220,
        "y": 3645
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 48
        }
      },
      "position": {
        "x": 2400,
        "y": 3540
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MODULO",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "modulo": "=MODULO(my_action.message, 5)"
        }
      },
      "position": {
        "x": 2400,
        "y": 3645
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 3

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 67.28
        }
      },
      "position": {
        "x": 2580,
        "y": 3540
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MODULO",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "modulo": "=MODULO(my_action.message, 12)"
        }
      },
      "position": {
        "x": 2580,
        "y": 3645
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MONTH

```
MONTH(date)
```

Returns the month of the year for the specified date. Accepted Format: year-month-day (year/month/day)

**Categories:** Dates/Times

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "2022-03-19T17:49:01+0000"
  }
}
```

Formula:

```
MONTH(my_action.message)
```

Output:

```json
"3"
```

## Sample actions

```json
{
  "standardLibVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "2022-03-19T17:49:01+0000"
        }
      },
      "position": {
        "x": 1035,
        "y": 1080
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "MONTH",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "month": "=MONTH(my_action.message)"
        }
      },
      "position": {
        "x": 1035,
        "y": 1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### MSG_PARSE

```
MSG_PARSE(file, [extract_multiline_headers: FALSE], [single_body: TRUE])
```

Takes the contents of an .msg file (Outlook item/email) and parses out information such as `to`, `from`, `subject`, `attachments`, etc.

By default headers are truncated on newline characters. To parse full headers set the extract_multiline_headers optional parameter to TRUE.

By default the parsed email includes a single `body` field containing the HTML part if available, otherwise the text part. Set the single_body optional parameter to FALSE to replace `body` with separate `body_html` and `body_text` fields.

Output is consistent with `EML_PARSE`.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": {
      "file_upload_1": {
        "contents": "BASE64 Encoded .msg file contents",
        "display_logo": true,
        "name": "Sample MSG File.msg",
        "type": "application/vnd.ms-outlook"
      }
    }
  }
}
```

Formula:

```
MSG_PARSE(BASE64_DECODE(my_action.message.file_upload_1.contents))
```

Output:

```json
{
  "attachments": [],
  "body": "<html>\n<body></body>\n</html>\n\n",
  "cc": [],
  "date": "2017-06-01T15:24:31+00:00",
  "from": "from@domain.com",
  "headers": {
    "Content-Type": "multipart/alternative; boundary=\"----_=_NextPart_001_00c81914.63ca02d3\"",
    "Date": "Thu, 01 Jun 2017 15:24:31 +0000",
    "From": "from@domain.com",
    "Message-ID": "c58b1b52f61f4789ba40339c6e993440",
    "Mime-Version": "1.0",
    "Subject": "creating an outlook message file",
    "Thread-Topic": "creating an outlook message file",
    "To": "to@domain.com",
    "X-Unsent": "1"
  },
  "message_id": "c58b1b52f61f4789ba40339c6e993440",
  "subject": "creating an outlook message file",
  "to": [
    "to@domain.com"
  ]
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "New Page",
      "description": "",
      "options": {
        "fields": "",
        "mode": "success_page",
        "submissionMessage": "Thank you for your submission",
        "visibility": "tenant",
        "pages_background_color": "#F9F9F9",
        "page_logo": {
          "name": "nites.jpeg",
          "type": "image/jpeg",
          "contents": "iVBORw0KGgoAAAANSUhEUgAAAFQAAAAYCAYAAABk8drWAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAARCSURBVHgB7VhLctpAEB1k7LVyA/kEgRMEn8BQ5e8qcALjE1icAPsEkJW/VeATwA1QTmDlBCE7l3/kPblbTIiMJcVOeaFXBRrN9Mz0vOnu6ZExBQoUKFCgwAdByX4ZDAbuw8PDwWw2a+LVk+rAcZxgZWWl02g0QpMSl5eXXRlnvLq62kLfqcmIi4uLZqlU+lwulztJ/aGvJ/p+39nZ6cu8R3j30c/f3t7umP8MRwtXV1df7+/vb6iMmZNJVJ6enppom5ydnbVNCpAIjENZF786Fn1kMgLE1PDocRz0HyTJQKe2zNOjMbBO3o1sZgS2Ybw29KqYd0ZEKMg8AGl980zAS3BhqV0Sv0Qmsho8/iCQi5T6XED/WtJmoj7W9/b2Vgk9gXUGWM+JtoF4H/VdFEdK/HvB4UIxuZ+2A2SPlykFayLhXkJ9z/wDsJlHaTZld3fXR4ho7O3tHWudRbyrxC+Ca+L4rxFuyXlJ7c7j4yOtKcuuuXd3d+0XJvMkZPwFWpm4cV64sLTBawuGB/UYus7Pz7vUB3NOYLGxV+EsuIHrT2ydITNCn5/sxyfaKdO0x02Qo8wNY7Yt52ChmeOKraAN2ZxlyBxLZb6xFCuvxWN4UE361PGYYn1egli0KSQJxIy42VIfso959rCehplFOYYUkY0MCJvn68C5CDUJLi2ho7msExVa3Pk0QL9veJxIuZ3W0pkZIMNYR3GodSBjA3VVlsUAPK1HprCOuqoQpmHGRV3FzNfcQvZQpSx06YjcFx3fMW8E7mBK0W6egwEk+ObZKkjqIO0hR1Ih/0vfYeGhpmAwgLpUX4OkMQv7+/shCNV0i+GtBrlYX5Kn2QLjNTeCMVvby6KkZ7IhsF/E6jyTDhqDfZMBJAHzUHHGP1cOuR8mJ05PTz0zPzs2MfZM20BgLAfCXGzmEAYTWbN4YRPy3JQxNusauo1V3hF3yopg4T1TbISSB3nSKLhZgL6HLDN8oLxp3ga8vPSTfnD3MTcTzw1ypeHASI6NX88+5Jy1tTWmF1luMRw8voHIKeeZbHBTHGCJgGseW4dU7pySrm3m6w62trZa9o/kQcdrkTNwaw9c8fZVhcV+oqtb5FY0DDgSTw7TKgKT7+gkhH0jyQK6Tt4kG+7eMtmMIAasLj6ELULqtsfw8oJ1jdA+YGjgLYvvPCcoR84Yc63+UWiIxucf78HiSsuUZFvLTpgF73rzSAI3FMS0MnQJtUCSmD/KdwvdGFfyygnb5NZIBGI8ShzTpwnzUcpaWU2oh1p8ytOVmDIsxIlImOkB0w/9AGFDFhaabJhCmcNlH0zgYoGxXHKxHW451LSF86vXQPdQ6sYqy7BmhYlofs7NPpImaZumR5y3jzVvsFLI0nW6ko9WRG6oaVg0v/nA0JCwjHhx1aktw3jGA+yF8dykr2ZisRVsZLg4XpIcSGWOG+b5ilagQIECBZLxG5sJ41WbSDouAAAAAElFTkSuQmCC"
        }
      },
      "position": {
        "x": 330,
        "y": 3360
      },
      "type": "form",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": {
        "fields": [
          {
            "content": null,
            "defaultValue": null,
            "description": "",
            "maxCharacters": null,
            "multiSelect": false,
            "name": "File upload 1",
            "options": [
              "Option 1",
              "Option 2"
            ],
            "required": false,
            "type": "FILE_UPLOAD"
          },
          {
            "content": "Submit",
            "defaultValue": null,
            "description": "",
            "maxCharacters": null,
            "multiSelect": false,
            "name": "Button",
            "options": [
              "Option 1",
              "Option 2"
            ],
            "required": false,
            "type": "BUTTON"
          }
        ]
      }
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "=new_page.body"
        }
      },
      "position": {
        "x": 330,
        "y": 3645
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "MSG_PARSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "msg_parse": "=MSG_PARSE(BASE64_DECODE(my_action.message.file_upload_1.contents))"
        }
      },
      "position": {
        "x": 330,
        "y": 3735
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### NEAT_JSON

```
NEAT_JSON(object)
```

Formats and "pretty prints" an object in JSON.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "object": {
      "city": "Melbourne",
      "country": "Australia",
      "post_code": 3000,
      "state": "Victoria"
    }
  }
}
```

Formula:

```
NEAT_JSON(my_action.object)
```

Output:

```json
"{\n \"city\": \"Melbourne\",\n \"state\": \"Victoria\",\n \"post_code\": 3000,\n \"country\": \"Australia\"\n}"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object": {
            "city": "Melbourne",
            "state": "Victoria",
            "post_code": 3000,
            "country": "Australia"
          }
        }
      },
      "position": {
        "x": 675,
        "y": 3390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "NEAT_JSON",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "neat_json": "=NEAT_JSON(my_action.object)"
        }
      },
      "position": {
        "x": 675,
        "y": 3495
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### NEWLINE_TO_BR

```
NEWLINE_TO_BR(text)
```

Replaces every newline (\n) with an HTML line break (&lt;br&gt;).

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello\nWorld\n!"
  }
}
```

Formula:

```
NEWLINE_TO_BR(my_action.message)
```

Output:

```json
"Hello<br />\nWorld<br />\n!"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "text": "Hello martin,\n\nYou hit a phishing site.\n\nDon't click on anything\n"
        }
      },
      "position": {
        "x": -6690,
        "y": -1275
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "NEWLINE TO BR",
      "description": "",
      "options": {
        "recipients": [
          "recipients@email.com"
        ],
        "reply_to": "replyto@email.com",
        "sender_name": "Sender Name",
        "subject": "Example email from Tines",
        "body": "<<NEWLINE_TO_BR(my_action.text)>>"
      },
      "position": {
        "x": -6690,
        "y": -1200
      },
      "type": "email",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "NEWLINE TO BR",
      "description": "",
      "options": {
        "recipients": [
          "recipients@email.com"
        ],
        "reply_to": "replyto@email.com",
        "sender_name": "Sender Name",
        "subject": "Example email from Tines",
        "body": "<<REPLACE(my_action.text, \"\\n\", \"<br>\")>>"
      },
      "position": {
        "x": -6465,
        "y": -1200
      },
      "type": "email",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "NEWLINE TO BR",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "<<NEWLINE_TO_BR(my_action.text)>>"
        }
      },
      "position": {
        "x": -6930,
        "y": -1200
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 3
    }
  ],
  "diagramNotes": []
}
```

#### NOT

```
NOT(any)
```

Returns the logical opposite of the supplied param.

**Categories:** Logic

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
NOT(my_action.value>100)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
NOT(my_action.value<100)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value": 5
        }
      },
      "position": {
        "x": -150,
        "y": 2205
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "NOT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "not": "=NOT(my_action.value>100)"
        }
      },
      "position": {
        "x": -225,
        "y": 2280
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "NOT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "not": "=NOT(my_action.value<100)"
        }
      },
      "position": {
        "x": -60,
        "y": 2280
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### NOW

```
NOW()
```

Returns the current date and time in UTC.

**Categories:** Dates/Times

## Examples

### Example 1

Formula:

```
NOW()
```

Output:

```json
"2025-05-14T09:21:09.483+00:00"
```

## Sample actions

```json
{
  "standardLibVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "NOW",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "now": "=NOW()"
        }
      },
      "position": {
        "x": 1290,
        "y": 1110
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### NUMBER

```
NUMBER(value)
```

Converts the passed value to a number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "100"
  }
}
```

Formula:

```
NUMBER(my_action.message)
```

Output:

```json
100
```

### Example 2: Also works with negative numbers and floats.

Input:

```json
{
  "my_action": {
    "message": "-10.1"
  }
}
```

Formula:

```
NUMBER(my_action.message)
```

Output:

```json
-10.1
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "100"
        }
      },
      "position": {
        "x": 2820,
        "y": 3540
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "NUMBER",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "number": "=NUMBER(my_action.message)"
        }
      },
      "position": {
        "x": 2820,
        "y": 3645
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### OBJECT

```
OBJECT(key1, value1, key2, value2, ...)
```

Returns an object with keys and values as specified.

**Categories:** Objects

## Examples

### Example 1

Formula:

```
OBJECT("city", "Melbourne", "state", "Victoria", "post_code", 3000, "country", "Australia")
```

Output:

```json
{
  "city": "Melbourne",
  "country": "Australia",
  "post_code": 3000,
  "state": "Victoria"
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "location": {
      "city": "Melbourne",
      "country": "Australia",
      "post_code": 3000,
      "state": "Victoria"
    },
    "person": {
      "age": 45,
      "name": "John Smith"
    },
    "registered": true
  }
}
```

Formula:

```
OBJECT("Attendee", my_action.person.name, "Location", my_action.location.city, "Status", IF(my_action.registered=TRUE, "Registered", "Not Registered"))
```

Output:

```json
{
  "Attendee": "John Smith",
  "Location": "Melbourne",
  "Status": "Registered"
}
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "OBJECT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object": "=OBJECT(\"city\", \"Melbourne\", \"state\", \"Victoria\", \"post_code\", 3000, \"country\", \"Australia\")"
        }
      },
      "position": {
        "x": 465,
        "y": 2505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "person": {
            "name": "John Smith",
            "age": 45
          },
          "location": {
            "city": "Melbourne",
            "state": "Victoria",
            "post_code": 3000,
            "country": "Australia"
          },
          "registered": true
        }
      },
      "position": {
        "x": 660,
        "y": 2505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "OBJECT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object": "=OBJECT(\"Attendee\", my_action.person.name, \"Location\", my_action.location.city, \"Status\", IF(my_action.registered=TRUE, \"Registered\", \"Not Registered\"))"
        }
      },
      "position": {
        "x": 660,
        "y": 2595
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### OBJECTS_TO_CSV

```
OBJECTS_TO_CSV(objects, headers)
```

Parses an array of objects with the same keys into CSV-formatted text. Headers are optional, if none are provided the keys of the first object are used.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "objects": [
      {
        "age": 45,
        "city": "Melbourne",
        "name": "John Smith"
      },
      {
        "age": 32,
        "city": "Sydney",
        "name": "Jane Doe"
      },
      {
        "age": 27,
        "city": "Brisbane",
        "name": "Bob Jones"
      }
    ]
  }
}
```

Formula:

```
OBJECTS_TO_CSV(my_action.objects)
```

Output:

```json
"name, age, city\nJohn Smith, 45, Melbourne\nJane Doe, 32, Sydney\nBob Jones, 27, Brisbane\n"
```

### Example 2

Input:

```json
{
  "my_action": {
    "objects": [
      {
        "age": 45,
        "city": "Melbourne",
        "name": "John Smith"
      },
      {
        "age": 32,
        "city": "Sydney",
        "name": "Jane Doe",
        "address": "123 Main St"
      },
      {
        "age": 27,
        "city": "Brisbane",
        "name": "Bob Jones"
      }
    ]
  }
}
```

Formula:

```
OBJECTS_TO_CSV(my_action.objects, ["name", "age", "address"])
```

Output:

```json
"name, age, address\nJohn Smith, 45,\nJane Doe, 32, 123 Main St\nBob Jones, 27,\n"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "objects": [
            {
              "name": "John Smith",
              "age": 45,
              "address": "Melbourne"
            },
            {
              "name": "Jane Doe",
              "age": 32,
              "address": "Sydney"
            },
            {
              "name": "Bob Jones",
              "age": 27,
              "city": "Brisbane"
            }
          ]
        }
      },
      "position": {
        "x": 3120,
        "y": 2055
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "OBJECTS_TO_CSV",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "objects_to_csv": "=OBJECTS_TO_CSV(my_action.objects)"
        }
      },
      "position": {
        "x": 3120,
        "y": 2145
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### OBJECTS_TO_MARKDOWN_TABLE

```
OBJECTS_TO_MARKDOWN_TABLE(objects, headers)
```

Converts an array of objects into a markdown-formatted table string. Headers are optional; if none are provided, the keys of the first object are used. Pipe characters in values are escaped automatically.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "objects": [
      {
        "name": "John Smith",
        "age": 45,
        "city": "Melbourne"
      },
      {
        "name": "Jane Doe",
        "age": 32,
        "city": "Sydney"
      },
      {
        "name": "Bob Jones",
        "age": 27,
        "city": "Brisbane"
      }
    ]
  }
}
```

Formula:

```
OBJECTS_TO_MARKDOWN_TABLE(my_action.objects)
```

Output:

```json
"| name | age | city |\n| --- | --- | --- |\n| John Smith | 45 | Melbourne |\n| Jane Doe | 32 | Sydney |\n| Bob Jones | 27 | Brisbane |"
```

### Example 2

Input:

```json
{
  "my_action": {
    "objects": [
      {
        "name": "John Smith",
        "age": 45,
        "city": "Melbourne"
      },
      {
        "name": "Jane Doe",
        "age": 32,
        "city": "Sydney"
      },
      {
        "name": "Bob Jones",
        "age": 27,
        "city": "Brisbane"
      }
    ]
  }
}
```

Formula:

```
OBJECTS_TO_MARKDOWN_TABLE(my_action.objects, ["name", "city"])
```

Output:

```json
"| name | city |\n| --- | --- |\n| John Smith | Melbourne |\n| Jane Doe | Sydney |\n| Bob Jones | Brisbane |"
```

#### OPENSSL_DECRYPT

```
OPENSSL_DECRYPT(ciphertext, algorithm, key, iv, [aad], [auth_tag])
```

Decrypts encrypted data using OpenSSL. 

The output is your plaintext. Arguments:

encrypted: The encrypted ciphertext.

algorithm: The algorithm to use, one of ["aes-128-cbc", "aes-256-cbc", "aes-256-gcm", "aes-256-ecb", "aes-128-ecb"].

key: The key to decrypt with.

iv: The initialization vector used during encryption. Value must be set to "" or nil when algorithm does not support an IV (such as AES ECB).

aad: The unencrypted data to be authenticated. Only supported by AEAD algorithms.

auth_tag: The authentication tag. Only supported by AEAD algorithms.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
=OPENSSL_DECRYPT(input.ciphertext, "aes-256-cbc", CREDENTIAL.secret_key, input.iv)
```

## Sample actions

```json
{
  "standardLibVersion": "69",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "OPENSSL_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "openssl_encrypt": "=OPENSSL_ENCRYPT(my_action.message, \"aes-256-gcm\", CREDENTIAL.aes_secret_key)"
        }
      },
      "position": {
        "x": 1620,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 1725,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "openssl_encrypt": "=OPENSSL_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key)"
        }
      },
      "position": {
        "x": 1800,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### OPENSSL_ENCRYPT

```
OPENSSL_ENCRYPT(text, algorithm, key, [aad])
```

Encrypts text using OpenSSL. Choose OPENSSL_ENCRYPT when you need to guarantee details of encryption, choose TINES_ENCRYPT/TINES_DECRYPT when performing both encryption and decryption within Tines for the best safety and features.

The output is an object with the ciphertext, the iv (if algorithm supports it), aad and auth_tag (if algorithm supports it). IV generation is handled via OpenSSL cryptographic random number generator using standard IV sized based on the algorithm provided. Values in the object are base64 encoded.

Arguments:

plaintext: The text to be encrypted.

algorithm: The algorithm to use, one of ["aes-128-cbc", "aes-256-cbc", "aes-256-gcm", "aes-256-ecb", "aes-128-ecb"] (recommended algorithm is aes-256-gcm)

key: The key to use for encryption. Note that each algorithm will require their own specific key sizes or an error will be raised.

aad: The 'additional authenticated data', which is unencrypted but will be authenticated when using a supported algorithm such as aes-256-gcm.

Returns an object with these keys:

encrypted: The ciphertext, base64 encoded.

iv: The IV if algorithm supports it. base64 encoded if present.

auth_tag: The authentication tag if using an AEAD algorithm such as aes-gcm-256, base64 encoded.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
OPENSSL_ENCRYPT("my message", "aes-256-cbc", CREDENTIAL.secret_key)
```

## Sample actions

```json
{
  "standardLibVersion": "69",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "OPENSSL_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "openssl_encrypt": "=OPENSSL_ENCRYPT(my_action.message, \"aes-256-gcm\", CREDENTIAL.aes_secret_key)"
        }
      },
      "position": {
        "x": 1620,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 1725,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "AES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "openssl_encrypt": "=OPENSSL_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key)"
        }
      },
      "position": {
        "x": 1800,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### OR

```
OR(value1, value2, ...)
```

Returns true if any of the arguments are truthy, otherwise returns false.

**Categories:** Logic

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
OR(my_action.value>5, my_action.value<100)
```

Output:

```json
true
```

### Example 2

Input:

```json
{
  "my_action": {
    "value": 5
  }
}
```

Formula:

```
OR(my_action.value<5, my_action.value>100)
```

Output:

```json
false
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "value": 5
        }
      },
      "position": {
        "x": 180,
        "y": 1935
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "OR",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "or": "=OR(my_action.value>5, my_action.value<100)"
        }
      },
      "position": {
        "x": 105,
        "y": 2010
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "OR",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "or": "=OR(my_action.value<5, my_action.value>100)"
        }
      },
      "position": {
        "x": 270,
        "y": 2010
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### PAD_LEFT

```
PAD_LEFT(input, target_length, pad_value)
```

Prepends specified text to the beginning of the input text until it reaches the defined target length. The target length cannot exceed 65536.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "my_text": "input"
  }
}
```

Formula:

```
PAD_LEFT(my_action.my_text, 10, "a")
```

Output:

```json
"aaaaainput"
```

### Example 2

Input:

```json
{
  "my_action": {
    "my_text": "input"
  }
}
```

Formula:

```
PAD_LEFT(my_action.my_text, 10, "abc")
```

Output:

```json
"abcabinput"
```

#### PAD_RIGHT

```
PAD_RIGHT(input, target_length, pad_value)
```

Appends specified text to the end of the input text until it reaches the defined target length. The target length cannot exceed 65536.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "my_text": "input"
  }
}
```

Formula:

```
PAD_RIGHT(my_action.my_text, 10, "a")
```

Output:

```json
"inputaaaaa"
```

### Example 2

Input:

```json
{
  "my_action": {
    "my_text": "input"
  }
}
```

Formula:

```
PAD_RIGHT(my_action.my_text, 10, "abc")
```

Output:

```json
"inputabcab"
```

#### PARSE_URL

```
PARSE_URL(text, [include_public_suffix_domains: FALSE])
```

Returns an object that representing the parsed url

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "url": "http://www.tines.com/test?foo=bar&baz=a&baz=b#123"
  }
}
```

Formula:

```
PARSE_URL(my_action.url)
```

Output:

```json
{
  "domain": "tines.com",
  "fragment": "123",
  "host": "www.tines.com",
  "path": "/test",
  "port": 80,
  "query": {
    "baz": [
      "a",
      "b"
    ],
    "foo": "bar"
  },
  "scheme": "http",
  "tld": "com"
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "url": "https://s3.us-east-2.amazonaws.com"
  }
}
```

Formula:

```
PARSE_URL(my_action.url, include_public_suffix_domains: TRUE)
```

Output:

```json
{
  "scheme": "https",
  "host": "s3.us-east-2.amazonaws.com",
  "path": "",
  "port": 443,
  "query": null,
  "fragment": null,
  "domain": "amazonaws.com",
  "tld": "com"
}
```

#### PEM_TO_JWK

```
PEM_TO_JWK(pem, [include_private: FALSE])
```

Converts a PEM-encoded key or X.509 certificate to JSON Web Key (JWK) format. By default returns only the public key components. When `include_private` is `TRUE`, includes private key components if available. Supports RSA and ECDSA keys, as well as extracting public keys from certificates.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "pem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0vx7agoebGcQSuuPiLJX\nZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tS\noc/BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt\n7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0\nzgdAZHzu6qMQvRL5hajrn1n91CbOpbISO_BIZ1tOl6U7QElTqLaXyJjnfwJTQwKP\nUwD3xQGNzqXPv2VZKtlkPKhEjRXN9YXJnS9QsQFRU5L2rN2tOXJm4a-BsxoYPAmC\nl1h\nwIDAQAB\n-----END PUBLIC KEY-----\n"
  }
}
```

Formula:

```
PEM_TO_JWK(my_action.pem)
```

Output:

```json
{
  "kty": "RSA",
  "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISO_BIZ1tOl6U7QElTqLaXyJjnfwJTQwKPUwD3xQGNzqXPv2VZKtlkPKhEjRXN9YXJnS9QsQFRU5L2rN2tOXJm4a-BsxoYPAmCl1h",
  "e": "AQAB"
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "pem": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDS_Htqih5sZxBK\n64-IslZmm032ed2tCZtcSmzZqIAVtaEzvwuFbHhxtt8AC1VPzrPC7VErtq8UXG6E\nNHUvq1Khz8EkQI95tYpFeMFkKIVXifeSSeeEyy2fri1n_Zb7kmwZjgdzmf3IFcCv\nCX3eWq3v9E3nDoJ_SHhDJDm_7rlgaNBHT8UNbZC_OpjfrxBAyJwC1pKrOzwolmCd\nhv1zt3TOB0BkfO7qoxC9EvmFqOufWf3UJs6lshI78EhnW06XpTtASVOotpfImOd_\nAlNDAo9TAPfFAY3Opc-_ZVkq2WQ8qESNFc31hcmdL1CxAVFTkvas3a05cmbhr4Gz\nGhg8CYKXWHAgMBAAECggEAX4cTI5uEHYj85CiGCJOg_E7Dh0t2zp2Px3KT-H-dN1n\nzCXJKt9-IQKJQ0h7RQxd1AgMBAAECggEAX4cTI5uEHYj85CiGCJOg_E7Dh0t2zp2P\nx3KT-H-dN1nzCXJKt9-IQKJQ0h7RQxd1AgMBAAECggEAX4cTI5uEHYj85CiGCJOg_\nE7Dh0t2zp2Px3KT-H-dN1nzCXJKt9-IQKJQ0h7RQxd1\n-----END PRIVATE KEY-----\n"
  }
}
```

Formula:

```
PEM_TO_JWK(my_action.pem, include_private: TRUE)
```

Output:

```json
{
  "kty": "RSA",
  "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISO_BIZ1tOl6U7QElTqLaXyJjnfwJTQwKPUwD3xQGNzqXPv2VZKtlkPKhEjRXN9YXJnS9QsQFRU5L2rN2tOXJm4a-BsxoYPAmCl1h",
  "e": "AQAB",
  "d": "X4cTI5uEHYj85CiGCJOg_E7Dh0t2zp2Px3KT-H-dN1nzCXJKt9-IQKJQ0h7RQxd1"
}
```

#### PLURALIZE

```
PLURALIZE(counter, text, [plural_text])
```

Outputs the singular or plural version of text based on the value of a number.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 2
  }
}
```

Formula:

```
PLURALIZE(my_action.message, "item")
```

Output:

```json
"items"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 1
  }
}
```

Formula:

```
PLURALIZE(my_action.message, "item")
```

Output:

```json
"item"
```

### Example 3: For custom pluralization, you can pass a third argument:

Input:

```json
{
  "my_action": {
    "message": 5
  }
}
```

Formula:

```
PLURALIZE(my_action.message, "bonus",  "bonuses")
```

Output:

```json
"bonuses"
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 2
        }
      },
      "position": {
        "x": 1830,
        "y": 3840
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "PLURALIZE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "pluralize": "=PLURALIZE(my_action.message, \"item\")"
        }
      },
      "position": {
        "x": 1830,
        "y": 3945
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 1
        }
      },
      "position": {
        "x": 2010,
        "y": 3840
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "PLURALIZE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "pluralize": "=PLURALIZE(my_action.message, \"item\")"
        }
      },
      "position": {
        "x": 2010,
        "y": 3945
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 3

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 5
        }
      },
      "position": {
        "x": 2190,
        "y": 3840
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "PLURALIZE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "pluralize": "=PLURALIZE(my_action.message, \"bonus\", \"bonuses\")"
        }
      },
      "position": {
        "x": 2190,
        "y": 3945
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### PLUS

```
PLUS(number, number)
```

Adds a number to another number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 4
  }
}
```

Formula:

```
PLUS(my_action.message, 2)
```

Output:

```json
6
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 67.28
  }
}
```

Formula:

```
PLUS(my_action.message, 12)
```

Output:

```json
79.28
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 4
        }
      },
      "position": {
        "x": 2445,
        "y": 3840
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "PLUS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "plus": "=PLUS(my_action.message, 2)"
        }
      },
      "position": {
        "x": 2445,
        "y": 3945
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 67.28
        }
      },
      "position": {
        "x": 2625,
        "y": 3840
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "PLUS",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "plus": "=PLUS(my_action.message, 12)"
        }
      },
      "position": {
        "x": 2625,
        "y": 3945
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### POWER

```
POWER(base, exponent)
```

Raises a number to the power of another number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 3
  }
}
```

Formula:

```
POWER(my_action.message, 2)
```

Output:

```json
9
```

### Example 2

Formula:

```
POWER(2, 3)
```

Output:

```json
8
```

## Sample actions

```json
{
  "standardLibVersion": "81",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 3
        }
      },
      "position": {
        "x": 2445,
        "y": 3840
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "POWER",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "power": "=POWER(my_action.message, 2)"
        }
      },
      "position": {
        "x": 2445,
        "y": 3945
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### PREPEND

```
PREPEND(text, prefix)
```

Adds the specified text to the beginning of the other text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "red, green, and blue"
  }
}
```

Formula:

```
PREPEND(my_action.message, "Primary colors: ")
```

Output:

```json
"Primary colors: red, green, and blue"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "red, green, and blue"
        }
      },
      "position": {
        "x": 2865,
        "y": 3840
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "PREPEND",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "prepend": "=PREPEND(my_action.message, \"Primary colors: \")"
        }
      },
      "position": {
        "x": 2865,
        "y": 3945
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### PROMPT

```
PROMPT("text")
```

Emit a URL that when visited will cause the current action to emit a new event. Text is optional, if provided will be available in the status field of the emitted event. [Prompt docs](https://www.tines.com/docs/prompts). The text must be URL encoded as it's included as a query parameter in the URL.

**Categories:** Story Metadata

## Examples

### Example 1

Formula:

```
PROMPT()
```

Output:

```json
"http://your.domain.com/prompt?a=2011&e=&s="
```

### Example 2

Formula:

```
PROMPT("OK")
```

Output:

```json
"http://your.domain.com/prompt?a=2011&e=&s=OK"
```

#### PUSH

```
PUSH(array, item1, ...)
```

Adds one or more items to the end of an array.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "array": [
      "red",
      "green"
    ]
  }
}
```

Formula:

```
PUSH(my_action.array, "blue")
```

Output:

```json
[
  "red",
  "green",
  "blue"
]
```

### Example 2: Takes one or more items as arguments.

Formula:

```
PUSH([1, 2, 3], 4, 5, 6)
```

Output:

```json
[
  1,
  2,
  3,
  4,
  5,
  6
]
```

### Example 3: Will add any item to the array, even another array. If you want to join two arrays together see CONCAT.

Formula:

```
PUSH([1, 2, 3], [4, 5, 6])
```

Output:

```json
[
  1,
  2,
  3,
  [
    4,
    5,
    6
  ]
]
```

#### RANDOM

```
RANDOM(min, max)
```

Returns a random number in the range specified.

**Categories:** Numbers

## Examples

### Example 1: Returns an random number between 1 and 10:

Input:

```json
{
  "my_action": {
    "message": 10
  }
}
```

Formula:

```
RANDOM(1, my_action.message)
```

Output:

```json
7
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 10
        }
      },
      "position": {
        "x": 1860,
        "y": 4140
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "RANDOM",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "random": "=RANDOM(1, my_action.message)"
        }
      },
      "position": {
        "x": 1860,
        "y": 4245
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### RANDOM_ELEMENT

```
RANDOM_ELEMENT(array)
```

Select a random element from an array.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
RANDOM_ELEMENT(my_action.message)
```

Output:

```json
"east"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            "north",
            "south",
            "east",
            "west"
          ]
        }
      },
      "position": {
        "x": 2115,
        "y": 4140
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "RANDOM_ELEMENT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "random_element": "=RANDOM_ELEMENT(my_action.message)"
        }
      },
      "position": {
        "x": 2115,
        "y": 4245
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### RANDOM_STRING

```
RANDOM_STRING(length, mode, custom_characters)
```

Returns a string of random characters. The function takes three optional arguments: length, mode, and custom.

length determines the length of the string (default 16).

mode determines the type of characters in the string. The options are "alphanumeric", "custom", "hex", "letters", "numbers" (default "alphanumeric").

The custom_characters argument is only used when mode is set to "custom". It is a string of characters that will be used to generate the random string.

**Categories:** Text

## Examples

### Example 1: Returns a string of random upper and lowercase letters and numbers (a-z, A-Z, and 0-9).

Formula:

```
RANDOM_STRING()
```

Output:

```json
"aBcDeFgH12345678"
```

### Example 2: Returns a string of random alphanumeric characters of set length.

Formula:

```
RANDOM_STRING(32)
```

Output:

```json
"aBcDeFgHiJkLmNoP1234567890123456"
```

### Example 3: In "hex" mode, returns a string of random lowercase hexadecimal characters (0-9 and a-f).

Formula:

```
RANDOM_STRING(16, "hex")
```

Output:

```json
"aa1bb2cc3dd4ee5f"
```

### Example 4: In "letters" mode, returns a string of random uppercase and lowercase letters (a-z and A-Z).

Formula:

```
RANDOM_STRING(52, "letters")
```

Output:

```json
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
```

### Example 5: In "numbers" mode, returns a string of random digits (0-9).

Formula:

```
RANDOM_STRING(32, "numbers")
```

Output:

```json
"01234567890123456789012345678901"
```

### Example 6: In "custom" mode, returns a string of random characters from the provided string.

Formula:

```
RANDOM_STRING(10, "custom", "abc123")
```

Output:

```json
"a1b2c3a2c1"
```

### Example 7: "custom" mode also accepts special characters and emojis.

Formula:

```
RANDOM_STRING(10, "custom", "a1&👍")
```

Output:

```json
"a1&👍👍&1a&1"
```

#### RANGE

```
RANGE(start, end)
```

Returns an array of numbers with the first value equal to `start` and the last value equal to `end`.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 10
  }
}
```

Formula:

```
RANGE(7, my_action.message)
```

Output:

```json
[
  7,
  8,
  9,
  10
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 10
        }
      },
      "position": {
        "x": 2400,
        "y": 4140
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "RANGE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "range": "=RANGE(7, my_action.message)"
        }
      },
      "position": {
        "x": 2400,
        "y": 4245
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### REDUCE

```
REDUCE(array, LAMBDA(previous_value, current_value, [expr]), initial_value)
```

Iterates over elements in an array, applying a specified lambda function to accumulate a result. This function takes 3 arguments

`array`: The iterable to be reduced.

`LAMBDA`: A lambda function with two arguments (`previous` and `current`) that defines the reduction operation. Optionally, an expression `[expr]` can be included for additional computation.

`initial_value`: an initial value for the accumulator. The reduction starts with this value.

**Categories:** Lambdas

## Examples

### Example 1

Input:

```json
{
  "my_array": [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10
  ]
}
```

Formula:

```
REDUCE(my_array, LAMBDA(previous_value, current_value, PLUS(previous_value, current_value)), 0)
```

Output:

```json
55
```

### Example 2

Input:

```json
{
  "array_of_objects": [
    {
      "timestamp": "2022-02-15 12:30:00+0000"
    },
    {
      "user": "bob.smith"
    },
    {
      "src_ip": "192.168.1.100"
    },
    {
      "dest_ip": "172.16.1.23"
    },
    {
      "file_hash": "2968896cb4639d4c839ab802720266d35bb15be93e3a4055109aa3bf1779f591"
    },
    {
      "file_name": "executable.exe"
    }
  ]
}
```

Formula:

```
REDUCE(array_of_objects, LAMBDA(previous_value, current_value, MERGE(previous_value, current_value)), OBJECT())
```

Output:

```json
{
  "dest_ip": "172.16.1.23",
  "file_hash": "2968896cb4639d4c839ab802720266d35bb15be93e3a4055109aa3bf1779f591",
  "file_name": "executable.exe",
  "src_ip": "192.168.1.100",
  "timestamp": "2022-02-15 12:30:00+0000",
  "user": "bob.smith"
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "REDUCE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "=REDUCE(my_action.vals, LAMBDA(previous_value, current_value, MERGE(previous_value, current_value)), OBJECT())"
        }
      },
      "position": {
        "x": -6900,
        "y": -1515
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "vals": [
            {
              "name": "martin"
            },
            {
              "location": "ireland"
            }
          ]
        }
      },
      "position": {
        "x": -6900,
        "y": -1635
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### REGEX_EXTRACT

```
REGEX_EXTRACT(text, regex, [modifiers])
```

Returns an array of all the regex matches on the input text.

Optionally, include a string containing [Ruby modifiers](https://docs.ruby-lang.org/en/master/Regexp.html#class-Regexp-label-Modes) – e.g. `i` for case insensitive mode.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "The quick brown fox jumps over the lazy dog. It barked."
  }
}
```

Formula:

```
REGEX_EXTRACT(my_action.message, "[A-Z]")
```

Output:

```json
[
  "T",
  "I"
]
```

### Example 2

Input:

```json
{
  "example": "hello & HELLO"
}
```

Formula:

```
REGEX_EXTRACT(example, "hello", "i")
```

Output:

```json
[
  "hello",
  "HELLO"
]
```

### Example 3: Extracting multiple matching groups

Input:

```json
{
  "my_action": {
    "message": "see no evil, hear no evil, speak no evil"
  }
}
```

Formula:

```
REGEX_EXTRACT(my_action.message, "([a-z]+) no ([a-z]+)")
```

Output:

```json
[
  [
    "see",
    "evil"
  ],
  [
    "hear",
    "evil"
  ],
  [
    "speak",
    "evil"
  ]
]
```

### Example 4: A more complex example that extracts the first match and pipes it into another function.

Input:

```json
{
  "my_action": {
    "message": "This is an automatically generated message from Tines"
  }
}
```

Formula:

```
REGEX_EXTRACT(my_action.message, "(?<=This is)(.*)(?=generated)")[0][0] |> UPCASE(%)
```

Output:

```json
" AN AUTOMATICALLY "
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "The quick brown fox jumps over the lazy dog. It barked."
        }
      },
      "position": {
        "x": 2640,
        "y": 4155
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REGEX_EXTRACT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "regex_extract": "=REGEX_EXTRACT(my_action.message, \"[A-Z]\")"
        }
      },
      "position": {
        "x": 2640,
        "y": 4260
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "description": "A more complex example that also extracts the first match and pipes it further to a down case function.",
          "message": "This is an automatically generated message from Tines"
        }
      },
      "position": {
        "x": 2850,
        "y": 4155
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REGEX_EXTRACT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "regex_extract": "=REGEX_EXTRACT(my_action.message, \"(?<=This is)(.*)(?=generated)\")[0][0] |> UPCASE(%)"
        }
      },
      "position": {
        "x": 2850,
        "y": 4260
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### REGEX_REPLACE

```
REGEX_REPLACE(text, regex, replacement)
```

Replaces every occurrence of a regex match in text with the second argument.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "hello hello!"
  }
}
```

Formula:

```
REGEX_REPLACE(my_action.message, "h\w+", "goodbye")
```

Output:

```json
"goodbye goodbye!"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "hello world!"
        }
      },
      "position": {
        "x": 3090,
        "y": 4170
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REGEX_REPLACE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "regex_replace": "=REGEX_REPLACE(my_action.message, \"\\w+\", \"goodbye\")"
        }
      },
      "position": {
        "x": 3090,
        "y": 4260
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### REJECT

```
REJECT(array | object, values_to_remove | LAMBDA(arg1, [arg2], expr))
```

Rejects items from an array or an object by a lambda function or an array of values to remove.

If the target is an array, the lambda must take one argument: the value.

If the target is an object, the lambda can take one or two arguments. If the lambda takes one argument, the argument is the value. If the lambda takes two arguments, the first argument is the key and the second argument is the value.

**Categories:** Lambdas, Arrays, Objects

## Examples

### Example 1

Input:

```json
{
  "my_array": [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10
  ]
}
```

Formula:

```
REJECT(my_array, ARRAY(2, 4, 6, 8, 10))
```

Output:

```json
[
  1,
  3,
  5,
  7,
  9
]
```

### Example 2

Input:

```json
{
  "my_array": [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10
  ]
}
```

Formula:

```
REJECT(my_array, LAMBDA(item, item > 5))
```

Output:

```json
[
  1,
  2,
  3,
  4,
  5
]
```

### Example 3

Input:

```json
{
  "my_object": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4,
    "e": 5
  }
}
```

Formula:

```
REJECT(my_object, ARRAY(2, 4))
```

Output:

```json
{
  "a": 1,
  "c": 3,
  "e": 5
}
```

### Example 4: If the target is an object and the lambda takes one argument, the argument is the value.

Input:

```json
{
  "my_object": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4,
    "e": 5
  }
}
```

Formula:

```
REJECT(my_object, LAMBDA(value, value > 2))
```

Output:

```json
{
  "a": 1,
  "b": 2
}
```

### Example 5: If the target is an object and the lambda takes two arguments, the first argument is the key and the second argument is the value.

Input:

```json
{
  "my_object": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4,
    "e": 5
  }
}
```

Formula:

```
REJECT(my_object, LAMBDA(key, value, key = 'a' || value > 3))
```

Output:

```json
{
  "b": 2,
  "c": 3
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "REJECT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "reject": "=REJECT(my_action.object_array, LAMBDA(object, INCLUDES(object.name, 'Martin')))"
        }
      },
      "position": {
        "x": -6555,
        "y": -1305
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object_array": [
            {
              "name": "Martin"
            },
            {
              "name": "Shelby"
            },
            {
              "name": "Thomas"
            }
          ]
        }
      },
      "position": {
        "x": -6555,
        "y": -1440
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### REMOVE

```
REMOVE(text, text_to_remove)
```

Removes every occurrence of the specified sub-text from text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "The car carted carrots"
  }
}
```

Formula:

```
REMOVE(my_action.message, "car")
```

Output:

```json
"The  ted rots"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "REMOVE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "remove": "=REMOVE(my_action.flat_array, 'apple')"
        }
      },
      "position": {
        "x": -6600,
        "y": -1245
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "flat_array": [
            "apple",
            "pear",
            "orange",
            "grapes"
          ],
          "object_array": [
            {
              "name": "Martin"
            },
            {
              "name": "Shelby"
            },
            {
              "name": "Thomas"
            }
          ],
          "nested_array": [
            [
              "cat",
              "turtle"
            ],
            [
              "dog"
            ]
          ],
          "array_with_null": [
            "apple",
            null,
            "banana",
            "grape",
            ""
          ]
        }
      },
      "position": {
        "x": -6600,
        "y": -1380
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### REMOVE_FIRST

```
REMOVE_FIRST(text, text_to_remove)
```

Removes the first occurrence of the specified sub-text from text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "The car carted carrots"
  }
}
```

Formula:

```
REMOVE_FIRST(my_action.message, "car")
```

Output:

```json
"The  carted carrots"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "The car carted carrots"
        }
      },
      "position": {
        "x": 1830,
        "y": 4455
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REMOVE_FIRST",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "remove_first": "=REMOVE_FIRST(my_action.message, \"car\")"
        }
      },
      "position": {
        "x": 1830,
        "y": 4560
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### REMOVE_KEY

```
REMOVE_KEY(object, path)
```

Removes the specified key from an object. Path is a dot separated path to the object, you can escape dots using backslash

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "person": {
      "email": "kubrik@movies.com",
      "name": "Stanley"
    }
  }
}
```

Formula:

```
REMOVE_KEY(my_action, "person.email")
```

Output:

```json
{
  "person": {
    "name": "Stanley"
  }
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "person": {
      "email": "kubrik@movies.com",
      "first.name": "Stanley"
    }
  }
}
```

Formula:

```
REMOVE_KEY(my_action, "person.first\.name")
```

Output:

```json
{
  "person": {
    "email": "kubrik@movies.com"
  }
}
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "person": {
            "name": "Stanley",
            "email": "kubrik@movies.com"
          }
        }
      },
      "position": {
        "x": 2115,
        "y": 4455
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REMOVE_KEY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "remove_key": "=REMOVE_KEY(my_action, \"person.email\")"
        }
      },
      "position": {
        "x": 2115,
        "y": 4560
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "person": {
            "first.name": "Stanley",
            "email": "kubrik@movies.com"
          }
        }
      },
      "position": {
        "x": 2295,
        "y": 4455
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REMOVE_KEY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "remove_key": "=REMOVE_KEY(my_action, \"person.first\\.name\")"
        }
      },
      "position": {
        "x": 2295,
        "y": 4560
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### REPLACE

```
REPLACE(text, search, replace_with)
```

Replaces every occurrence of the search text with the replacement text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "We will build what we need"
  }
}
```

Formula:

```
REPLACE(my_action.message, "we", "I")
```

Output:

```json
"We will build what I need"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "We will build what we need"
        }
      },
      "position": {
        "x": 2445,
        "y": 4455
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REPLACE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "replace": "=REPLACE(my_action.message, \"we\", \"I\")"
        }
      },
      "position": {
        "x": 2445,
        "y": 4560
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### REPLACE_FIRST

```
REPLACE_FIRST(text, search, replace_with)
```

Replaces the first occurrence of the search text with the replacement text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "we will build what we need"
  }
}
```

Formula:

```
REPLACE_FIRST(my_action.message, "we", "I")
```

Output:

```json
"I will build what we need"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "REPLACE_FIRST",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "replace_first": "=REPLACE_FIRST(my_action.message, \"we\", \"I\")"
        }
      },
      "position": {
        "x": 2685,
        "y": 4560
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "we will build what we need"
        }
      },
      "position": {
        "x": 2685,
        "y": 4455
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### REVERSE

```
REVERSE(array)
```

Reverses the order of the elements in an array. Please note, this operation will recursively flatten arrays and directly nested arrays, then reverse the final array.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
REVERSE(my_action.message)
```

Output:

```json
[
  "west",
  "east",
  "south",
  "north"
]
```

### Example 2: REVERSE can't directly reverse an individual piece of text, but you can combine it with SPLIT and JOIN to make it work:

Input:

```json
{
  "my_action": {
    "message": "edit maps"
  }
}
```

Formula:

```
JOIN(REVERSE(SPLIT(my_action.message, "")), "")
```

Output:

```json
"spam tide"
```

### Example 3: REVERSE also flattens arrays

Input:

```json
{
  "my_action": {
    "message": [
      [
        1,
        2,
        3
      ],
      [
        4,
        5,
        6
      ],
      [
        7,
        8,
        9
      ]
    ]
  }
}
```

Formula:

```
REVERSE(my_action.message)
```

Output:

```json
[
  9,
  8,
  7,
  6,
  5,
  4,
  3,
  2,
  1
]
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            "north",
            "south",
            "east",
            "west"
          ]
        }
      },
      "position": {
        "x": 2955,
        "y": 4455
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REVERSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "reverse": "=REVERSE(my_action.message)"
        }
      },
      "position": {
        "x": 2955,
        "y": 4560
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "edit maps"
        }
      },
      "position": {
        "x": 1140,
        "y": -3480
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "REVERSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "reverse": "=REVERSE(my_action.message)"
        }
      },
      "position": {
        "x": 1140,
        "y": -3375
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### ROTATE

```
ROTATE(array, steps)
```

Rotates the elements in an array by any number of steps.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "array": [
    "north",
    "south",
    "east",
    "west"
  ]
}
```

Formula:

```
ROTATE(array)
```

Output:

```json
[
  "south",
  "east",
  "west",
  "north"
]
```

### Example 2: Accepts a second argument to rotate by a number of steps. Goes to the provided index, then moves everything following that index to the beginning of the array.

Input:

```json
{
  "array": [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10
  ]
}
```

Formula:

```
ROTATE(array, 7)
```

Output:

```json
[
  8,
  9,
  10,
  1,
  2,
  3,
  4,
  5,
  6,
  7
]
```

### Example 3: Accepts negative numbers to rotate in the opposite direction

Input:

```json
{
  "array": [
    "apple",
    "banana",
    "cherry",
    "date"
  ]
}
```

Formula:

```
ROTATE(array, -1)
```

Output:

```json
[
  "date",
  "apple",
  "banana",
  "cherry"
]
```

## Sample actions

```json
{
  "standardLibVersion": "36",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "ROTATE",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "<<ROTATE(my_action.my_action.array1, 4)>>"
        }
      },
      "position": {
        "x": -195,
        "y": 375
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "cloud:8b3d0c1d536d7aef6416b3d0e57a460a:6c8cd17db8bb0a3868840ff9d61a890b"
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "my_action": {
            "array1": [
              1,
              2,
              3,
              4,
              5,
              6
            ]
          }
        }
      },
      "position": {
        "x": -195,
        "y": 270
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "recordType": null,
      "recordWriters": [],
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null,
      "originStoryIdentifier": "cloud:8b3d0c1d536d7aef6416b3d0e57a460a:6c8cd17db8bb0a3868840ff9d61a890b"
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### ROUND

```
ROUND(number, [precision])
```

Rounds an input number to the nearest whole number or, if precision is specified, to that number of decimal places.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 3.3
  }
}
```

Formula:

```
ROUND(my_action.message)
```

Output:

```json
3
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 6.6
  }
}
```

Formula:

```
ROUND(my_action.message)
```

Output:

```json
7
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": 2435.135433
  }
}
```

Formula:

```
ROUND(my_action.message, 2)
```

Output:

```json
2435.14
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "down": 3.3,
          "up": 6.6,
          "limited": 2435.135433
        }
      },
      "position": {
        "x": 1470,
        "y": -3495
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "ROUND",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "round_down": "=ROUND(my_action.down)",
          "round_up": "=ROUND(my_action.up)",
          "round_limited": "=ROUND(my_action.limited, 2)"
        }
      },
      "position": {
        "x": 1470,
        "y": -3390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### RSA_AES_HYBRID_DECRYPT

```
RSA_AES_HYBRID_DECRYPT(encrypted_data_array, key, [padding: "PKCS1_PADDING"])
```

Decrypts data that was encrypted with `RSA_AES_HYBRID_ENCRYPT`, using a provided public or private RSA key. A padding choosing from "PKCS1_PADDING", "PKCS1_OAEP_PADDING" and "SSLV23_PADDING" can be passed in with "PKCS1_PADDING" being the default.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
RSA_AES_HYBRID_DECRYPT(encrypted_data_array, key)
```

Output:

```json
"Decrypted message"
```

### Example 2

Formula:

```
RSA_AES_HYBRID_DECRYPT(encrypted_data_array, key, padding: "PKCS1_PADDING")
```

Output:

```json
"Decrypted message"
```

#### RSA_AES_HYBRID_ENCRYPT

```
RSA_AES_HYBRID_ENCRYPT(plain_message, key, [padding: "PKCS1_PADDING"])
```

Encrypts data of arbitrary length using a provided public or private RSA key, in a hybrid cryptosystem internally using AES. A padding choosing from "PKCS1_PADDING", "PKCS1_OAEP_PADDING" and "SSLV23_PADDING" can be passed in with "PKCS1_PADDING" being the default.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
RSA_AES_HYBRID_ENCRYPT(plain_message, key)
```

Output:

```json
[
  "(encrypted aes key)",
  "(encrypted initialization vector)",
  "(encrypted data)"
]
```

### Example 2

Formula:

```
RSA_AES_HYBRID_ENCRYPT(plain_message, key, padding: "PKCS1_PADDING")
```

Output:

```json
[
  "(encrypted aes key)",
  "(encrypted initialization vector)",
  "(encrypted data)"
]
```

#### RSA_DECRYPT

```
RSA_DECRYPT(encrypted_message, key, [padding: "PKCS1_PADDING"])
```

Decrypts encrypted data using a public or private key. A padding choosing from "PKCS1_PADDING", "PKCS1_OAEP_PADDING" and "SSLV23_PADDING" can be passed in with "PKCS1_PADDING" being the default.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
RSA_DECRYPT(encrypted_data, key)
```

Output:

```json
"Decrypted message"
```

### Example 2

Formula:

```
RSA_DECRYPT(encrypted_data, key, padding: "PKCS1_PADDING")
```

Output:

```json
"Decrypted data"
```

## Sample actions

```json
{
  "standardLibVersion": "20",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "pems",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "=GENERATE_RSA_KEYS()"
        }
      },
      "position": {
        "x": 855,
        "y": -675
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "encrypt message",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "<<RSA_ENCRYPT(\"my message\",pems.message[1]) |> BASE64_ENCODE(%)>>"
        }
      },
      "position": {
        "x": 855,
        "y": -555
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "decrypt message",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "<<encrypt_message.message |> BASE64_DECODE(%) |> RSA_DECRYPT(%, pems.message[0])>>"
        }
      },
      "position": {
        "x": 855,
        "y": -450
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### RSA_ENCRYPT

```
RSA_ENCRYPT(plain_message, key, [padding: "PKCS1_PADDING"])
```

Encrypts data using a public or private key. A padding choosing from "PKCS1_PADDING", "PKCS1_OAEP_PADDING" and "SSLV23_PADDING" can be passed in with "PKCS1_PADDING" being the default.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
RSA_ENCRYPT(plain_message, key)
```

Output:

```json
"Encrypted data"
```

### Example 2

Formula:

```
RSA_ENCRYPT(plain_message, key, padding: "PKCS1_PADDING")
```

Output:

```json
"Encrypted data"
```

## Sample actions

```json
{
  "standardLibVersion": "20",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "pems",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "=GENERATE_RSA_KEYS()"
        }
      },
      "position": {
        "x": 855,
        "y": -675
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "encrypt message",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "<<RSA_ENCRYPT(\"my message\",pems.message[1]) |> BASE64_ENCODE(%)>>"
        }
      },
      "position": {
        "x": 855,
        "y": -555
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### RSA_SIGN

```
RSA_SIGN(data, key, [digest: "SHA256"])
```

Signs data using a private key. A digest choosing from "SHA256", "SHA384", and "SHA512" can be passed in with "SHA256" being the default.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
RSA_SIGN(data, key, digest: "SHA256")
```

Output:

```json
"Signed data"
```

#### RSTRIP

```
RSTRIP(text)
```

Removes all whitespace (tabs, spaces, and newlines) from the right side of text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "       Hello World!      "
  }
}
```

Formula:

```
RSTRIP(my_action.message)
```

Output:

```json
"       Hello World!"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "RSTRIP",
      "description": "You'll see that the white space after \"martin\" was removed, but the white space before \"hello\" was not.",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "rstrip": "=RSTRIP(my_action.string)"
        }
      },
      "position": {
        "x": -6570,
        "y": -1275
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "Click before and after \"hello my name is martin\" and you will see there is white space.",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": " hello my name is martin "
        }
      },
      "position": {
        "x": -6570,
        "y": -1365
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### SECOND

```
SECOND(date)
```

Returns the second of the minute for the specified date.

**Categories:** Dates/Times

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "2022-03-19T17:49:01+0000"
  }
}
```

Formula:

```
SECOND(my_action.message)
```

Output:

```json
1
```

## Sample actions

```json
{
  "standardLibraryVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "2022-03-19T17:49:01+0000"
        }
      },
      "position": {
        "x": 1035,
        "y": 1080
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "SECOND",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "second": "=SECOND(my_action.message)"
        }
      },
      "position": {
        "x": 1035,
        "y": 1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### SET_KEY

```
SET_KEY(object, path, value)
```

Sets an object key to a value. If the key already exists, it will be overwritten. Nested keys can be specified using dot notation.

**Categories:** Objects

## Examples

### Example 1: Adds new keys

Formula:

```
SET_KEY({"name": "Marvin"}, "hair", "brown")
```

Output:

```json
{
  "hair": "brown",
  "name": "Marvin"
}
```

### Example 2: Overwrites keys

Formula:

```
SET_KEY({"name": "Marvin", "age": 30}, "age", 31)
```

Output:

```json
{
  "age": 31,
  "name": "Marvin"
}
```

### Example 3: Works on nested object paths

Formula:

```
SET_KEY({"name": "Marvin", "job": {"company": "Tines", "role": "Software Engineer"}}, "job.role", "Manager")
```

Output:

```json
{
  "job": {
    "company": "Tines",
    "role": "Manager"
  },
  "name": "Marvin"
}
```

### Example 4: Escape dots with backslash

Formula:

```
SET_KEY({".ie": "Ireland"}, "\.co\.uk", "United Kingdom")
```

Output:

```json
{
  ".co.uk": "United Kingdom",
  ".ie": "Ireland"
}
```

#### SHA1

```
SHA1(text)
```

Calculates the sha1 hash of text.

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
SHA1(my_action.message)
```

Output:

```json
"8ef7e6b365afb1e01ec7c1a04cd505bbb5ee700a"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Hello World"
        }
      },
      "position": {
        "x": 285,
        "y": 3105
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SHA1",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "sha1": "=SHA1(my_action.message)"
        }
      },
      "position": {
        "x": 285,
        "y": 3195
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### SHA256

```
SHA256(text)
```

Calculates the sha256 hash of text, expressed in hex.

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
SHA256(my_action.message)
```

Output:

```json
"f23127ca63dbfea2d0535bafee7957bab11a0a50e67cb7b24adec0d3736c47ff"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "SHA256",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "sha256": "=SHA256(my_action.string)"
        }
      },
      "position": {
        "x": -7050,
        "y": -1530
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": "Apples are the best fruit"
        }
      },
      "position": {
        "x": -7050,
        "y": -1635
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### SHA256_BASE64

```
SHA256_BASE64(text)
```

Calculates the sha256 hash of the text, expressed in base64.

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
SHA256_BASE64(my_action.message)
```

Output:

```json
"8jEnymPb/qLQU1uv7nlXurEaClDmfLeySt7A03NsR/8="
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "SHA256_BASE64",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "sha256_base64": "=SHA256_BASE64(my_action.string)"
        }
      },
      "position": {
        "x": -6780,
        "y": -1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": "Apples are the best fruit"
        }
      },
      "position": {
        "x": -6780,
        "y": -1305
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### SHA512

```
SHA512(text)
```

Calculates the sha512 hash of text.

**Categories:** Hashing/Signing

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "This is a private message for your eyes only!"
  }
}
```

Formula:

```
SHA512(my_action.message)
```

Output:

```json
"82753b40c835cf78396ba0a35e089eb61cd27a6dcdd9ef98c7b01fbfa02e65ff0de600076641889060d3fcca093a237b862dceba856d9c14fc9381c154088ff5"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Hello World"
        }
      },
      "position": {
        "x": 990,
        "y": 3105
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SHA512",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "sha512": "=SHA512(my_action.message)"
        }
      },
      "position": {
        "x": 990,
        "y": 3195
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### SHUFFLE

```
SHUFFLE(array)
```

Shuffles all the elements in an array

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      1,
      2,
      3,
      4
    ]
  }
}
```

Formula:

```
SHUFFLE(my_action.message)
```

Output:

```json
[
  4,
  1,
  3,
  2
]
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
SHUFFLE(my_action.message)
```

Output:

```json
[
  "east",
  "north",
  "west",
  "south"
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            1,
            2,
            3,
            4
          ]
        }
      },
      "position": {
        "x": 2265,
        "y": 4755
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            "north",
            "south",
            "east",
            "west"
          ]
        }
      },
      "position": {
        "x": 2415,
        "y": 4755
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SHUFFLE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "shuffle": "=SHUFFLE(my_action.message)"
        }
      },
      "position": {
        "x": 2340,
        "y": 4860
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### SIN

```
SIN(x)
```

Computes the sine of x (in radians), returning a number between -π/2 and π/2

**Categories:** Numbers

#### SIZE

```
SIZE(text_or_array)
```

Returns the number of characters in text or the number of elements in an array.

**Categories:** Text, Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
SIZE(my_action.message)
```

Output:

```json
"4"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "north"
  }
}
```

Formula:

```
SIZE(my_action.message)
```

Output:

```json
"5"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "Notice there is a blank line in the array. If you click \"Plain code\", you'll see it shows \"\" and that is counted when using SIZE.",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array": [
            "array item 1",
            "array item 2",
            "array item 3",
            ""
          ],
          "string": "hello world!"
        }
      },
      "position": {
        "x": 2130,
        "y": -2955
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SIZE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array_size": "=SIZE(my_action.array)",
          "string_size": "=SIZE(my_action.string)"
        }
      },
      "position": {
        "x": 2130,
        "y": -2835
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### SLICE

```
SLICE(text, start_index, [length])
```

Returns 1 or `length` characters from a piece of text, beginning at the `start_index`.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
SLICE(my_action.message, 6)
```

Output:

```json
"w"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
SLICE(my_action.message, 6, 5)
```

Output:

```json
"world"
```

### Example 3: If `length` extends past the end of the piece of text, it returns only the remainder of the text:

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
SLICE(my_action.message, 6, 50)
```

Output:

```json
"world"
```

### Example 4: `start_index` can be specified as a negative number to count back from the end of the piece of text:

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
SLICE(my_action.message, -5, 5)
```

Output:

```json
"world"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "hello world"
        }
      },
      "position": {
        "x": 2790,
        "y": 4755
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SLICE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "slice": "=SLICE(my_action.message, 6)"
        }
      },
      "position": {
        "x": 2565,
        "y": 4860
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SLICE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "slice": "=SLICE(my_action.message, 6, 5)"
        }
      },
      "position": {
        "x": 2715,
        "y": 4860
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SLICE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "slice": "=SLICE(my_action.message, 6, 50)"
        }
      },
      "position": {
        "x": 2865,
        "y": 4860
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SLICE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "slice": "=SLICE(my_action.message, -5, 5)"
        }
      },
      "position": {
        "x": 3015,
        "y": 4860
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 3
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 4
    }
  ],
  "diagramNotes": []
}
```

#### SLICE_ARRAY

```
SLICE_ARRAY(array, start_index, [length])
```

Returns the remainder of the array (or `length` elements of the array, if specified), beginning at the `start_index`.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
SLICE_ARRAY(my_action.message, 2)
```

Output:

```json
[
  "east",
  "west"
]
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
SLICE_ARRAY(my_action.message, 1, 2)
```

Output:

```json
[
  "south",
  "east"
]
```

### Example 3: If `length` extends past the end of the array, it returns only the remainder of the array:

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
SLICE_ARRAY(my_action.message, 1, 50)
```

Output:

```json
[
  "south",
  "east",
  "west"
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            "north",
            "south",
            "east",
            "west"
          ]
        }
      },
      "position": {
        "x": 1845,
        "y": 5070
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SLICE_ARRAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "slice_array": "=SLICE_ARRAY(my_action.message, 2)"
        }
      },
      "position": {
        "x": 1695,
        "y": 5175
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SLICE_ARRAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "slice_array": "=SLICE_ARRAY(my_action.message, 1, 2)"
        }
      },
      "position": {
        "x": 1845,
        "y": 5175
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SLICE_ARRAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "slice_array": "=SLICE_ARRAY(my_action.message, 1, 50)"
        }
      },
      "position": {
        "x": 1995,
        "y": 5175
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 3
    }
  ],
  "diagramNotes": []
}
```

#### SORT

```
SORT(array, path, [include_json_paths: FALSE])
```

Sorts elements in an array by a property of an element in the array (case-sensitive). Pass a dot-separated path to sort by nested keys.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "South",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
SORT(my_action.message)
```

Output:

```json
[
  "South",
  "east",
  "north",
  "west"
]
```

### Example 2: Sorts by key

Input:

```json
{
  "animals": [
    {
      "name": "Dog"
    },
    {
      "name": "Cat"
    },
    {
      "name": "Bear"
    }
  ]
}
```

Formula:

```
SORT(animals, 'name')
```

Output:

```json
[
  {
    "name": "Bear"
  },
  {
    "name": "Cat"
  },
  {
    "name": "Dog"
  }
]
```

### Example 3: Sorts by nested key

Input:

```json
{
  "employees": [
    {
      "name": "Jim",
      "address": {
        "city": "New York"
      }
    },
    {
      "name": "John",
      "address": {
        "city": "Dublin"
      }
    },
    {
      "name": "Jane",
      "address": {
        "city": "London"
      }
    }
  ]
}
```

Formula:

```
SORT(employees, 'address.city', include_json_paths: TRUE)
```

Output:

```json
[
  {
    "name": "John",
    "address": {
      "city": "Dublin"
    }
  },
  {
    "name": "Jane",
    "address": {
      "city": "London"
    }
  },
  {
    "name": "Jim",
    "address": {
      "city": "New York"
    }
  }
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "SORT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "sorted": "=SORT(my_action.arr1)"
        }
      },
      "position": {
        "x": -6570,
        "y": -1350
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "arr1": [
            "dog",
            "cat",
            "Llama",
            "BEAR",
            "4"
          ]
        }
      },
      "position": {
        "x": -6570,
        "y": -1455
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### SORT_NATURAL

```
SORT_NATURAL(array, path, [include_json_paths: FALSE])
```

Sorts elements in an array by a property of an element in the array (case-insensitive). Pass a dot-separated path to sort by nested keys.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "South",
      "east",
      "west"
    ]
  }
}
```

Formula:

```
SORT_NATURAL(my_action.message)
```

Output:

```json
[
  "east",
  "north",
  "South",
  "west"
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "SORT NATURAL",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "sort_natural": "=SORT_NATURAL(my_action.arr1)"
        }
      },
      "position": {
        "x": -6585,
        "y": -1395
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "arr1": [
            "dog",
            "cat",
            "Llama",
            "BEAR",
            "4"
          ]
        }
      },
      "position": {
        "x": -6585,
        "y": -1515
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### SPLIT

```
SPLIT(text, delimiter)
```

Divides the input text into an array using the delimiter as a separator

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "one,two,three,four,five"
  }
}
```

Formula:

```
SPLIT(my_action.message, ",")
```

Output:

```json
[
  "one",
  "two",
  "three",
  "four",
  "five"
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "one,two,three,four,five"
        }
      },
      "position": {
        "x": 2580,
        "y": -2865
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SPLIT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "split": "=SPLIT(my_action.message, \",\")",
          "split_and_call_index": "=SPLIT(my_action.message, \",\")[2]"
        }
      },
      "position": {
        "x": 2580,
        "y": -2775
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### SQRT

```
SQRT(x)
```

Computes the square root of a given number

**Categories:** Numbers

#### STARTS_WITH

```
STARTS_WITH(text, prefix)
```

Returns true if the text begins with the prefix

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
STARTS_WITH(my_action.message, "Hello")
```

Output:

```json
"true"
```

### Example 2: case-sensitive

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
STARTS_WITH(my_action.message, "hello")
```

Output:

```json
"false"
```

#### STORY_RUN_GUID

```
STORY_RUN_GUID()
```

Returns the GUID of the currently executing story run.

**Categories:** Story Metadata

## Examples

### Example 1

Formula:

```
STORY_RUN_GUID()
```

Output:

```json
"<<guid>>"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "STORY_RUN_GUID",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "story_run_guid": "=STORY_RUN_GUID()"
        }
      },
      "position": {
        "x": -210,
        "y": 3765
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### STORY_RUN_LINK

```
STORY_RUN_LINK([mode: "story_runs"])
```

Returns a link to the currently executing story run. Mode can be set to "action" to return a link to the action with events filtered by story run.

**Categories:** Story Metadata

## Examples

### Example 1

Formula:

```
STORY_RUN_LINK()
```

Output:

```json
"https://<<tenant>>.tines.com/stories/<<story_id>>/runs/<<guid>>/"
```

### Example 2

Formula:

```
STORY_RUN_LINK("action")
```

Output:

```json
"https://<<tenant>>.tines.com/stories/<<story_id>>?actions=<<action_id>>&storyRuns=<<guid>>"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "STORY_RUN_LINK",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "story_run_link": "=STORY_RUN_LINK()"
        }
      },
      "position": {
        "x": 30,
        "y": 3765
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### STRIP

```
STRIP(text)
```

Removes all whitespace (tabs, spaces, and newlines) from both the left and right side of text.

It does not affect spaces between words.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "       Hello World!      "
  }
}
```

Formula:

```
STRIP(my_action.message)
```

Output:

```json
"Hello World!"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "STRIP",
      "description": "STRIP removes all whitespace",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "strip": "=STRIP(my_action.string)"
        }
      },
      "position": {
        "x": -6660,
        "y": -1320
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "Click before \"hello\" and after \"martin\" and you will see the white space.",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "string": " hello my name is martin "
        }
      },
      "position": {
        "x": -6660,
        "y": -1425
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### STRIP_HTML

```
STRIP_HTML(text)
```

Removes any HTML tags from text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "<p>Hello <br /><strong>World!</strong></p>"
  }
}
```

Formula:

```
STRIP_HTML(my_action.message)
```

Output:

```json
"Hello World!"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "<p>Hello <br /><strong>World!</strong></p>"
        }
      },
      "position": {
        "x": 2505,
        "y": 5085
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "STRIP_HTML",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "strip_html": "=STRIP_HTML(my_action.message)"
        }
      },
      "position": {
        "x": 2505,
        "y": 5175
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### STRIP_NEWLINES

```
STRIP_NEWLINES(text)
```

Removes any newline characters (line breaks) from text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello \nWorld\n!"
  }
}
```

Formula:

```
STRIP_NEWLINES(my_action.message)
```

Output:

```json
"Hello World!"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Hello \nWorld\n!"
        }
      },
      "position": {
        "x": 2760,
        "y": 5100
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "STRIP_NEWLINES",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "strip_newlines": "=STRIP_NEWLINES(my_action.message)"
        }
      },
      "position": {
        "x": 2760,
        "y": 5190
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### SUM

```
SUM(array)
```

Sums the elements in an array of numbers.

**Categories:** Arrays, Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      1,
      2,
      3,
      4
    ]
  }
}
```

Formula:

```
SUM(my_action.message)
```

Output:

```json
10
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            1,
            2,
            3,
            4
          ]
        }
      },
      "position": {
        "x": 3015,
        "y": 5070
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "SUM",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "sum": "=SUM(my_action.message)"
        }
      },
      "position": {
        "x": 3015,
        "y": 5175
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### SWITCH

```
SWITCH(expression, value_to_compare, value_to_return, ..., default_value)
```

Compares the input expression against a list of values and returns the corresponding result for the listed value upon first match. If no match is found, the default values is returned.

**Categories:** Logic

## Examples

### Example 1

Formula:

```
SWITCH(1+1, 2, 'true!', 1, 'false!', 'error!')
```

Output:

```json
"true!"
```

### Example 2

Input:

```json
{
  "day_of_week": "sunday"
}
```

Formula:

```
SWITCH(day_of_week, 'monday', 'working', 'saturday', 'weekend', 'error')
```

Output:

```json
"error"
```

### Example 3

Input:

```json
{
  "status": "approved"
}
```

Formula:

```
SWITCH(status, 'pending', 'yellow', 'approved', 'green', 'rejected', 'red', 'gray')
```

Output:

```json
"green"
```

### Example 4

Input:

```json
{
  "score": "F"
}
```

Formula:

```
SWITCH(score, 'A', 'Excellent', 'B', 'Good', 'C', 'Average', 'Needs Improvement')
```

Output:

```json
"Needs Improvement"
```

#### TALLY

```
TALLY(array, [strict_types: FALSE])
```

Counts the occurrences of each unique element within an array, accounting for case sensitivity.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "dog",
      "dog",
      "cat",
      "cat",
      "cat",
      "fish"
    ]
  }
}
```

Formula:

```
TALLY(my_action.message)
```

Output:

```json
{
  "dog": 2,
  "cat": 3,
  "fish": 1
}
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": [
      404,
      404,
      "404",
      505,
      "505"
    ]
  }
}
```

Formula:

```
TALLY(my_action.message, strict_types: TRUE)
```

Output:

```json
[
  {
    "key": 404,
    "count": 2
  },
  {
    "key": "404",
    "count": 1
  },
  {
    "key": 505,
    "count": 1
  },
  {
    "key": "505",
    "count": 1
  }
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": [
            "dog",
            "dog",
            "cat",
            "cat",
            "cat",
            "fish"
          ]
        }
      },
      "position": {
        "x": 3015,
        "y": 5070
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TALLY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "tally": "=TALLY(my_action.message)"
        }
      },
      "position": {
        "x": 3015,
        "y": 5175
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TAR

```
TAR(compressed_file_data)
```

Creates a tarball for a list of files

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "zip_files": [
    {
      "name": "example.txt",
      "contents": "hello, world!"
    },
    {
      "name": "other.csv",
      "contents": "1,2,3"
    }
  ]
}
```

Formula:

```
TAR(files)
```

#### TEXT

```
TEXT(value)
```

Converts the passed value to text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 100
  }
}
```

Formula:

```
TEXT(my_action.message)
```

Output:

```json
"100"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 100
        }
      },
      "position": {
        "x": 1830,
        "y": 5400
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TEXT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "text": "=TEXT(my_action.message)"
        }
      },
      "position": {
        "x": 1830,
        "y": 5505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TIMES

```
TIMES(number, number)
```

Multiplies a number by another number.

**Categories:** Numbers

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": 4
  }
}
```

Formula:

```
TIMES(my_action.message, 2)
```

Output:

```json
8
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 67.28
  }
}
```

Formula:

```
TIMES(my_action.message, 12)
```

Output:

```json
807.36
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 4
        }
      },
      "position": {
        "x": 2055,
        "y": 5400
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TIMES",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "times": "=TIMES(my_action.message, 2)"
        }
      },
      "position": {
        "x": 2055,
        "y": 5505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 67.28
        }
      },
      "position": {
        "x": 2220,
        "y": 5400
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TIMES",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "times": "=TIMES(my_action.message, 12)"
        }
      },
      "position": {
        "x": 2220,
        "y": 5505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TINES_DECRYPT

```
TINES_DECRYPT(encrypted, key)
```

Decrypts text using Tines encryption. TINES_DECRYPT enforces expiration: attempting to decrypt expired data raises an error. Expiration has second granularity and is subject to clock skew.
Arguments:

encrypted: The encrypted ciphertext must be the direct output of TINES_ENCRYPT.

key: A key of at least 32 bytes.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
TINES_DECRYPT(my_action.encrypted_message, CREDENTIAL.secret_key)
```

## Sample actions

```json
{
  "standardLibVersion": "70",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "otZMcEgEhODINSIB1g3Yz9lqUBZymCp7QiP0ajBeXFAWRfZerTYkDF5jormeYSBr"
        }
      },
      "position": {
        "x": 1980,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TINES_DECRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "tines_decrypt": "=TINES_DECRYPT(my_action.message, CREDENTIAL.aes_secret_key)"
        }
      },
      "position": {
        "x": 1980,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TINES_ENCRYPT

```
TINES_ENCRYPT(text, key, [expires_in])
```

Encrypts text using Tines encryption. TINES_ENCRYPT is intentionally abstract. Guarantees: authenticated encryption; safe key and IV management with randomized 192-bit IVs and negligible collision probability (no IV reuse even under very high volume); TINES_DECRYPT will always decrypt the output; and, when the OpenSSL FIPS provider is enabled, a FIPS 140–validated crypto module is used. If you need to know or control the exact algorithm, use OPENSSL_ENCRYPT instead.

Implementation note (non-binding; subject to change): currently AES-256-GCM via OpenSSL with a randomized 192-bit IV; a 96-bit segment is used in key derivation; and a SHA-256 HMAC commitment tag over the ciphertext, IV, and auth tag.
Arguments:

text: Plaintext to encrypt.

key: Text of at least 32 bytes. Longer keys are condensed to 32 bytes internally via domain separated HMAC.

expires_in: Integer seconds (>= 0). 0 expires immediately (second granularity; subject to clock skew). Negative values raise an error.

**Categories:** Encryption

## Examples

### Example 1

Formula:

```
TINES_ENCRYPT("hello world", CREDENTIAL.secret_key, expires_in: 60)
```

## Sample actions

```json
{
  "standardLibVersion": "70",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "TINES_ENCRYPT",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "tines_encrypt": "=TINES_ENCRYPT(my_action.message, CREDENTIAL.aes_secret_key, expires_in: 60)"
        }
      },
      "position": {
        "x": 1620,
        "y": 1830
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "This is a private message for your eyes only!"
        }
      },
      "position": {
        "x": 1725,
        "y": 1740
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### TITLEIZE

```
TITLEIZE(text)
```

Capitalizes the first letter of each word in a string and replaces some characters in the string to make the title prettier.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
TITLEIZE(my_action.message)
```

Output:

```json
"Hello World"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "helloWorld"
  }
}
```

Formula:

```
TITLEIZE(my_action.message)
```

Output:

```json
"Hello World"
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": "hello_world"
  }
}
```

Formula:

```
TITLEIZE(my_action.message)
```

Output:

```json
"Hello World"
```

#### TODAY

```
TODAY()
```

Returns the current date

**Categories:** Dates/Times

## Examples

### Example 1

Formula:

```
TODAY()
```

Output:

```json
"2023-04-04"
```

## Sample actions

```json
{
  "standardLibVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "TODAY",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "today": "=TODAY()"
        }
      },
      "position": {
        "x": 1290,
        "y": 1110
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### TO_CSV

```
TO_CSV(array_of_arrays)
```

Convert an array of arrays into CSV-formatted text.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": [
    [
      "city",
      "state"
    ],
    [
      "Melbourne",
      "VIC"
    ],
    [
      "Sydney",
      "NSW"
    ],
    [
      "New York City",
      "NY"
    ],
    [
      "Los Angeles",
      "CA"
    ]
  ]
}
```

Formula:

```
TO_CSV(my_action)
```

Output:

```json
"city, state\nMelbourne, VIC\nSydney, NSW\n\"New York City\", NY\n\"Los Angeles\", CA\n"
```

### Example 2

Formula:

```
TO_CSV([[1, 2], [3, 4], [5, 6]])
```

Output:

```json
"1, 2\n3, 4\n5, 6\n"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": [
          [
            "city",
            "state"
          ],
          [
            "Melbourne",
            "VIC"
          ],
          [
            "Sydney",
            "NSW"
          ],
          [
            "New York City",
            "NY"
          ],
          [
            "Los Angeles",
            "CA"
          ]
        ]
      },
      "position": {
        "x": 930,
        "y": 3390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TO_CSV",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "to_csv": "=TO_CSV(my_action)"
        }
      },
      "position": {
        "x": 930,
        "y": 3495
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TO_HEX

```
TO_HEX(text)
```

Converts text or data into hexadecimal text.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "text_data": "This is a piece of text"
  }
}
```

Formula:

```
TO_HEX(my_action.text_data)
```

Output:

```json
"546869732069732061207069656365206f662074657874"
```

### Example 2

Input:

```json
{
  "my_action": {
    "base64_binary_data": "Jvgw8kqvWFd2N7BTKFn4xA=="
  }
}
```

Formula:

```
TO_HEX(BASE64_DECODE(my_action.base64_binary_data))
```

Output:

```json
"26f830f24aaf58577637b0532859f8c4"
```

#### TO_JSON

```
TO_JSON(object)
```

Convert an object into JSON text.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "object": {
      "city": "Melbourne",
      "country": "Australia",
      "post_code": 3000,
      "state": "Victoria"
    }
  }
}
```

Formula:

```
TO_JSON(my_action.object)
```

Output:

```json
" {\"city\":\"Melbourne\",\"state\":\"Victoria\",\"post_code\":3000,\"country\":\"Australia\"}"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object": {
            "city": "Melbourne",
            "state": "Victoria",
            "post_code": 3000,
            "country": "Australia"
          }
        }
      },
      "position": {
        "x": 1185,
        "y": 3390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TO_JSON",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "to_json": "=TO_JSON(my_action.object)"
        }
      },
      "position": {
        "x": 1185,
        "y": 3495
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TO_SNAKE_CASE

```
TO_SNAKE_CASE(text, [from_camel_case: false])
```

Turn input text into snake case. When from_camel_case is true, the function will convert camel case to snake case.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
TO_SNAKE_CASE(my_action.message)
```

Output:

```json
"hello_world"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "Testing-1 2 3"
  }
}
```

Formula:

```
TO_SNAKE_CASE(my_action.message)
```

Output:

```json
"testing_1_2_3"
```

### Example 3

Input:

```json
{
  "my_action": {
    "message": "fooBar"
  }
}
```

Formula:

```
TO_SNAKE_CASE(my_action.message)
```

Output:

```json
"foobar"
```

### Example 4

Input:

```json
{
  "my_action": {
    "message": "fooBar"
  }
}
```

Formula:

```
TO_SNAKE_CASE(my_action.message, from_camel_case: true)
```

Output:

```json
"foo_bar"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Hello World!"
        }
      },
      "position": {
        "x": 2415,
        "y": 5400
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TO_SNAKE_CASE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "to_snake_case": "=TO_SNAKE_CASE(my_action.message)"
        }
      },
      "position": {
        "x": 2505,
        "y": 5505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Testing-1 2 3"
        }
      },
      "position": {
        "x": 2580,
        "y": 5400
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TO_XML

```
TO_XML(object, root_element_name = "hash")
```

Converts an object into XML, root element name is optional.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "object": {
      "city": "Melbourne",
      "country": "Australia",
      "post_code": 3000,
      "state": "Victoria"
    }
  }
}
```

Formula:

```
TO_XML(my_action.object)
```

Output:

```json
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n  <city>Melbourne</city>\n  <state>Victoria</state>\n  <post-code type=\"integer\">3000</post-code>\n  <country>Australia</country>\n</hash>\n"
```

### Example 2

Input:

```json
{
  "my_action": {
    "object": {
      "city": "Melbourne",
      "country": "Australia",
      "post_code": 3000,
      "state": "Victoria"
    }
  }
}
```

Formula:

```
TO_XML(my_action.object, "Data")
```

Output:

```json
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Data>\n  <city>Melbourne</city>\n  <state>Victoria</state>\n  <post-code type=\"integer\">3000</post-code>\n  <country>Australia</country>\n</Data>\n"
```

## Sample actions

```json
{
  "standardLibVersion": "15",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object": {
            "city": "Melbourne",
            "state": "Victoria",
            "post_code": 3000,
            "country": "Australia"
          }
        }
      },
      "position": {
        "x": 270,
        "y": 825
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TO_XML",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "to_xml": "=TO_XML(my_action.object)"
        }
      },
      "position": {
        "x": 270,
        "y": 930
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TO_YAML

```
TO_YAML(object)
```

Converts an object into YAML

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "object": {
      "city": "Melbourne",
      "country": "Australia",
      "post_code": 3000,
      "state": "Victoria"
    }
  }
}
```

Formula:

```
TO_YAML(my_action.object)
```

Output:

```json
"---\ncity: Melbourne\nstate: Victoria\npost_code: 3000\ncountry: Australia\n"
```

## Sample actions

```json
{
  "standardLibVersion": "15",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "object": {
            "city": "Melbourne",
            "state": "Victoria",
            "post_code": 3000,
            "country": "Australia"
          }
        }
      },
      "position": {
        "x": 480,
        "y": 825
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TO_YAML",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "to_yaml": "=TO_YAML(my_action.object)"
        }
      },
      "position": {
        "x": 480,
        "y": 930
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TRANSLITERATE

```
TRANSLITERATE(text)
```

Replaces non-ASCII characters with an ASCII approximation, or if none exists, a replacement character: “?”.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "À È Î Ô Û"
  }
}
```

Formula:

```
TRANSLITERATE(my_action.message)
```

Output:

```json
"A E I O U"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "Trademark - ™, Pound Sterling - £, Cents - ¢"
  }
}
```

Formula:

```
TRANSLITERATE(my_action.message)
```

Output:

```json
"Trademark - ?, Pound Sterling - ?, Cents - ?"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "À È Î Ô Û"
        }
      },
      "position": {
        "x": 585,
        "y": 1695
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "Trademark - ™, Pound Sterling - £, Cents - ¢"
        }
      },
      "position": {
        "x": 750,
        "y": 1695
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TRANSLITERATE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "transliterate": "=TRANSLITERATE(my_action.message)"
        }
      },
      "position": {
        "x": 675,
        "y": 1770
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 2
    },
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 2
    }
  ],
  "diagramNotes": []
}
```

#### TRUNCATE

```
TRUNCATE(text, length, [ellipsis])
```

Shorten text down to the number of characters passed as a parameter.

If the number of characters specified is less than the length of the text, `...` is appended to the text and is included in the character count.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
TRUNCATE(my_action.message, 8)
```

Output:

```json
"Hello..."
```

### Example 2: An optional third argument controls the characters inserted if the text is truncated (instead of `...`):

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
TRUNCATE(my_action.message, 9, " etc")
```

Output:

```json
"Hello etc"
```

### Example 3: Setting the third argument to `""` prevents any text being inserted if the text is truncated:

Input:

```json
{
  "my_action": {
    "message": "Hello World!"
  }
}
```

Formula:

```
TRUNCATE(my_action.message, 5, "")
```

Output:

```json
"Hello"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "TRUNCATE",
      "description": "-Truncate will use the assigned path: my_action.example\n\n-then the number is the number of characters (this includes any spaces and punctuation), so on the 29th character it will remove everything that follows\n\n-then it will replace everything that follows with whatever is in quotations, in this case a period \".\"",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "truncate": "=TRUNCATE(my_action.example, 29, \".\")"
        }
      },
      "position": {
        "x": -525,
        "y": 1005
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "Ignore everything after this! and replace with a period"
        }
      },
      "position": {
        "x": -525,
        "y": 885
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### TRUNCATEWORDS

```
TRUNCATEWORDS(text, length, [ellipsis])
```

Shortens text down to the number of words passed as the argument.

If the specified number of words is less than the number of words in the text, `...` is appended to the text.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello and welcome to Tines"
  }
}
```

Formula:

```
TRUNCATEWORDS(my_action.message, 3)
```

Output:

```json
"Hello and welcome..."
```

### Example 2: An optional third argument controls the characters inserted if the text is truncated (instead of `...`):

Input:

```json
{
  "my_action": {
    "message": "Hello and welcome to Tines"
  }
}
```

Formula:

```
TRUNCATEWORDS(my_action.message, 3, " etc")
```

Output:

```json
"Hello and welcome etc"
```

### Example 3: Setting the third argument to `""` prevents any text being inserted if the text is truncated:

Input:

```json
{
  "my_action": {
    "message": "Hello and welcome to Tines"
  }
}
```

Formula:

```
TRUNCATEWORDS(my_action.message, 3, "")
```

Output:

```json
"Hello and welcome"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "This is for a insert here"
        }
      },
      "position": {
        "x": 615,
        "y": 3975
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TRUNCATE WORDS",
      "description": "-Truncate words will use the assigned path: my_action.example\n\n-then the number is the number of WORDS to retain, then it replaces every word after word 4 in this example\n\n-then it will replace everything that follows word 4 with whatever is in quotations, in this case the word \"test\" will be the replacement of 'insert here'",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "Truncate_words": "=TRUNCATEWORDS(my_action.example, 4, \" test\")"
        }
      },
      "position": {
        "x": 615,
        "y": 4095
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### TYPE

```
TYPE(value)
```

Outputs the type (class) of the specified value, and supports the following data types:

| Result | Data Type          | Description                 | Example                             |
| :------------ | :----------------- | :-------------------------- | :---------------------------------- |
| `String`      | String             | Text values                 | `"Hello World"`                     |
| `Integer`     | Integer            | Whole numbers               | `42`                                |
| `Float`       | Float              | Decimal numbers             | `3.14`                              |
| `Array`       | Array              | Ordered collection of items | `[1, 2, 3]`                         |
| `Hash`        | Object             | Key-value pairs             | `{"name": "John", "age": 30}`       |
| `TrueClass`   | Boolean (true)     | Boolean true value          | `true`                              |
| `FalseClass`  | Boolean (false)    | Boolean false value         | `false`                             |
| `NilClass`    | Null               | Absence of a value          | `null`                              |

**Categories:** Other

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "hello"
  }
}
```

Formula:

```
TYPE(my_action.message)
```

Output:

```json
"\"String\""
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": 1
  }
}
```

Formula:

```
TYPE(my_action.message)
```

Output:

```json
"\"Integer\""
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "hello"
        }
      },
      "position": {
        "x": 2760,
        "y": 5400
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "TYPE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "type": "=TYPE(my_action.message)"
        }
      },
      "position": {
        "x": 2805,
        "y": 5505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": 1
        }
      },
      "position": {
        "x": 2910,
        "y": 5400
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### UNFLATTEN_JSON

```
UNFLATTEN_JSON(object, [separator])
```

Transforms a single layer of key/value pairs into a nested JSON structure. Default key separator is a period or full stop (`.`)

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "contact.address.0.city": "Huntsville",
  "contact.address.0.geo.lat": "2.243232",
  "contact.address.0.geo.lon": "1.23123",
  "contact.address.0.state": "NC",
  "contact.address.0.street": "101 3rd St",
  "contact.address.0.type": "home",
  "contact.address.1.city": "city",
  "contact.address.1.geo.lat": "2.243232",
  "contact.address.1.geo.lon": "1.23123",
  "contact.address.1.state": "NC",
  "contact.address.1.street": "15 Main St",
  "contact.address.1.type": "work",
  "contact.id": "557",
  "contact.name": "Jane Smith"
}
```

Formula:

```
UNFLATTEN_JSON(json)
```

Output:

```json
{
  "contact": {
    "address": [
      {
        "city": "Huntsville",
        "geo": {
          "lat": "2.243232",
          "lon": "1.23123"
        },
        "state": "NC",
        "street": "101 3rd St",
        "type": "home"
      },
      {
        "city": "city",
        "geo": {
          "lat": "2.243232",
          "lon": "1.23123"
        },
        "state": "NC",
        "street": "15 Main St",
        "type": "work"
      }
    ],
    "id": "557",
    "name": "Jane Smith"
  }
}
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "contact.address.0.city": "Huntsville",
          "contact.address.0.geo.lat": "2.243232",
          "contact.address.0.geo.lon": "1.23123",
          "contact.address.0.state": "NC",
          "contact.address.0.street": "101 3rd St",
          "contact.address.0.type": "home",
          "contact.address.1.city": "city",
          "contact.address.1.geo.lat": "2.243232",
          "contact.address.1.geo.lon": "1.23123",
          "contact.address.1.state": "NC",
          "contact.address.1.street": "15 Main St",
          "contact.address.1.type": "work",
          "contact.id": "557",
          "contact.name": "Jane Smith"
        }
      },
      "position": {
        "x": 3090,
        "y": 5415
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "UNFLATTEN_JSON",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "unflatten_json": "=UNFLATTEN_JSON(my_action)"
        }
      },
      "position": {
        "x": 3090,
        "y": 5505
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### UNION

```
UNION(array1, array2)
```

This function merges two arrays. These arrays can either be within the same Event or from two distinct Actions. The function requires that the inputs be arrays, although it can also process an array nested within an object, as long as it directly references the array. The order of the merged array is determined by the sequence in which the paths are listed.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "array1": [
      1,
      2,
      3
    ],
    "array2": [
      3,
      4,
      5
    ]
  }
}
```

Formula:

```
UNION(my_action.array1, my_action.array2)
```

Output:

```json
[
  1,
  2,
  3,
  4,
  5
]
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "2",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "array1": [
            1,
            2,
            3
          ],
          "array2": [
            3,
            4,
            5
          ]
        }
      },
      "position": {
        "x": 1635,
        "y": 2355
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "UNION",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": "=UNION(my_action.array1, my_action.array2)"
      },
      "position": {
        "x": 1635,
        "y": 2430
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### UNIQ

```
UNIQ(array)
```

Removes any duplicate elements in an array.

**Categories:** Arrays

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": [
      "north",
      "south",
      "east",
      "east",
      "east",
      "south",
      "west"
    ]
  }
}
```

Formula:

```
UNIQ(my_action.message)
```

Output:

```json
[
  "north",
  "south",
  "east",
  "west"
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": [
            "apple",
            "pear",
            "orange",
            "grapes",
            "apple",
            "apple"
          ]
        }
      },
      "position": {
        "x": -795,
        "y": 1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "UNIQ",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "uniq": "=UNIQ(my_action.example)"
        }
      },
      "position": {
        "x": -795,
        "y": 1290
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### UNIX_TIMESTAMP

```
UNIX_TIMESTAMP()
```

Returns the current unix timestamp, i.e. the number of seconds since midnight on 1 January 1970

**Categories:** Dates/Times

## Examples

### Example 1

Formula:

```
UNIX_TIMESTAMP()
```

Output:

```json
1647965833
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "UNIX_TIMESTAMP",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "unix": "=UNIX_TIMESTAMP()"
        }
      },
      "position": {
        "x": -6660,
        "y": -1440
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "time": "2022-11-21 16:00:00"
        }
      },
      "position": {
        "x": -6660,
        "y": -1530
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### UNTAR

```
UNTAR(tar_data)
```

Extracts a list of files from a tarball.

**Categories:** Data Parsing/Conversion

#### UNZIP

```
UNZIP(zip_data, [password])
```

Extracts files from a ZIP archive.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Formula:

```
UNZIP(BASE64_DECODE(input.zip_data), 'optional_password')
```

Output:

```json
[
  {
    "filename": "test_filename",
    "content_type": "application/octet-stream",
    "guid": "2634c668-7066-47e5-ad01-ed77781d5daf",
    "md5": "ccc55aefbf92aa66f42b638802c5e7f6",
    "sha256": "623447b85d372251f3ffce2602e142b25ba40ee641821beabe6e14530d1cdd49",
    "sizeinbytes": 13,
    "base64encodedcontents": "dGVzdF9jb250ZW50cw==",
    "path": "test_filename"
  }
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "UNZIP",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "unzip": "=UNZIP(BASE64_DECODE(my_action.base64encoded_file))"
        }
      },
      "position": {
        "x": 2820,
        "y": -2370
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "base64encoded_file": "UEsDBBQAAAAIAEE1Tlb6/ZIgEQAAABEAAAAEAAAAZmlsZQvJyCxWAKKS1IoShbTMnFQAUEsBAjQDFAAAAAgAQTVOVvr9kiARAAAAEQAAAAQAAAAAAAAAAQAAAKSBAAAAAGZpbGVQSwUGAAAAAAEAAQAyAAAAMwAAAAAA"
        }
      },
      "position": {
        "x": 2715,
        "y": -2490
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "base64encoded_file": "UEsDBBQAAAAIAMl4K1ZPTgbDEAAAAA4AAAAJAAAAZmlsZTEudHh0S8vMSY1Pzs8rSc0rKTYEAFBLAwQUAAAACADJgCtW9R8PWhAAAAAOAAAACQAAAGZpbGUyLnR4dEvLzEmNT87PK0nNKyk2AgBQSwECNAMUAAAACADJeCtWT04GwxAAAAAOAAAACQAAAAAAAAABAAAApIEAAAAAZmlsZTEudHh0UEsBAjQDFAAAAAgAyYArVvUfD1oQAAAADgAAAAkAAAAAAAAAAQAAAKSBNwAAAGZpbGUyLnR4dFBLBQYAAAAAAgACAG4AAABuAAAAAAA="
        }
      },
      "position": {
        "x": 2895,
        "y": -2490
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 1,
      "receiverIdentifier": 0
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### UPCASE

```
UPCASE(text)
```

Makes each character in text uppercase.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "Hello World"
  }
}
```

Formula:

```
UPCASE(my_action.message)
```

Output:

```json
"HELLO WORLD"
```

### Example 2

Input:

```json
{
  "my_action": {
    "message": "ALREADY UPPERCASE"
  }
}
```

Formula:

```
UPCASE(my_action.message)
```

Output:

```json
"ALREADY UPPERCASE"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "example": "make everything uppercase"
        }
      },
      "position": {
        "x": -45,
        "y": 915
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "UPCASE",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "upcase": "=UPCASE(my_action.example)"
        }
      },
      "position": {
        "x": -45,
        "y": 1035
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### URL_DECODE

```
URL_DECODE(text)
```

Decodes text that has been encoded as a URL or by `URL_ENCODE`.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "%27Stop%21%27+said+Fred"
  }
}
```

Formula:

```
URL_DECODE(my_action.message)
```

Output:

```json
"'Stop!' said Fred"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "%27Stop%21%27+said+Fred"
        }
      },
      "position": {
        "x": 2715,
        "y": 5730
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "URL_DECODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "url_decode": "=URL_DECODE(my_action.message)"
        }
      },
      "position": {
        "x": 2715,
        "y": 5820
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### URL_ENCODE

```
URL_ENCODE(text, [encoding: "form"])
```

Converts any URL-unsafe characters in the given text with form or uri encoding. Specify `encoding: "uri"` to use URI encoding, default encoding is form.

**Categories:** Text

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "alice@example.com"
  }
}
```

Formula:

```
URL_ENCODE(my_action.message)
```

Output:

```json
"alice%40example.com"
```

### Example 2: Space characters are turned into `+` characters instead of being percent encoded:

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
URL_ENCODE(my_action.message)
```

Output:

```json
"hello+world"
```

### Example 3: % encoding can be enabled via encoding: "uri":

Input:

```json
{
  "my_action": {
    "message": "hello world"
  }
}
```

Formula:

```
URL_ENCODE(my_action.message, encoding: "uri")
```

Output:

```json
"hello%20world"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "alice@example.com"
        }
      },
      "position": {
        "x": 645,
        "y": -2325
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "URL_ENCODE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "url_encode": "=URL_ENCODE(my_action.message)"
        }
      },
      "position": {
        "x": 570,
        "y": -2220
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "hello world"
        }
      },
      "position": {
        "x": 480,
        "y": -2325
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### UUID

```
UUID()
```

Generates a universally unique identifier (UUID)

**Categories:** Text

## Examples

### Example 1

Formula:

```
UUID()
```

Output:

```json
"17c1c2ac-e1fd-42c5-a5b1-16b117bfe882"
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "UUID",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "uuid": "=UUID()"
        }
      },
      "position": {
        "x": 945,
        "y": 1695
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [],
  "diagramNotes": []
}
```

#### VALUES

```
VALUES(object)
```

Returns all the values of an object.

**Categories:** Objects

## Examples

### Example 1

Input:

```json
{
  "my_object": {
    "color": "blue",
    "size": "large"
  }
}
```

Formula:

```
VALUES(my_object)
```

Output:

```json
[
  "blue",
  "large"
]
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": {
            "color": "blue",
            "size": "large"
          }
        }
      },
      "position": {
        "x": 2325,
        "y": 6045
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "VALUES",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "values": "=VALUES(my_action.message)"
        }
      },
      "position": {
        "x": 2325,
        "y": 6135
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### WHERE

```
WHERE(array, path, [value], [null_on_path_error: FALSE])
```

Takes an array of objects, a path and a value (optional). The path can be a single key or a dot-separated series of keys. Returns a new array containing only the objects where the value at the key/path matches the provided value. If value is not provided, it returns an array containing only the objects where the value at the key/path is [present](https://www.tines.com/docs/formulas/functions/is-present).

If you try to pass NULL in for value, it will be treated as not provided, so you cannot use this function to check for NULL values. Instead, you can use [FILTER](https://www.tines.com/docs/formulas/functions/filter) to find NULL values like so:

`FILTER(data, LAMBDA(object, object["key"] = NULL))`. To return NULL when path has a value of NULL, set the `null_on_path_error` parameter to TRUE. `null_on_path_error` is FALSE by default.

**Categories:** Arrays, Objects

## Examples

### Example 1

Input:

```json
{
  "staff": [
    {
      "name": "Alice",
      "department": "engineering"
    },
    {
      "name": "Bob",
      "department": "sales"
    },
    {
      "name": "Charlie",
      "department": "engineering"
    }
  ]
}
```

Formula:

```
WHERE(staff, "department", "engineering")
```

Output:

```json
[
  {
    "name": "Alice",
    "department": "engineering"
  },
  {
    "name": "Charlie",
    "department": "engineering"
  }
]
```

### Example 2

Input:

```json
{
  "staff": [
    {
      "name": "Alice",
      "department": {
        "name": "engineering",
        "role": "manager"
      }
    },
    {
      "name": "Bob",
      "department": {
        "name": "sales",
        "role": "assistant"
      }
    },
    {
      "name": "Charlie",
      "department": {
        "name": "sales",
        "role": "manager"
      }
    }
  ]
}
```

Formula:

```
WHERE(staff, "department.role", "manager")
```

Output:

```json
[
  {
    "name": "Alice",
    "department": {
      "name": "engineering",
      "role": "manager"
    }
  },
  {
    "name": "Charlie",
    "department": {
      "name": "sales",
      "role": "manager"
    }
  }
]
```

### Example 3: If 'value' isn't provided, return all objects where the path exists and the value at that path IS_PRESENT.

Input:

```json
{
  "staff": [
    {
      "name": "Alice",
      "department": {
        "name": "engineering",
        "role": "manager",
        "team": "AI"
      }
    },
    {
      "name": "Bob",
      "department": {
        "name": "sales",
        "role": "assistant",
        "team": "   "
      }
    },
    {
      "name": "Charlie"
    },
    {
      "name": "Denise",
      "department": {
        "name": "sales",
        "role": "manager"
      }
    }
  ]
}
```

Formula:

```
WHERE(data.staff, "department.team")
```

Output:

```json
[
  {
    "name": "Alice",
    "department": {
      "name": "engineering",
      "role": "manager",
      "team": "AI"
    }
  }
]
```

### Example 4: If 'path' is provided NULL and 'null_on_path_error' is provided TRUE, return NULL.

Input:

```json
{
  "staff": [
    {
      "name": "Alice",
      "department": {
        "name": "engineering",
        "role": "manager",
        "team": "AI"
      }
    }
  ]
}
```

Formula:

```
WHERE(data.staff, NULL, null_on_path_error: TRUE)
```

Output:

```json
"NULL"
```

## Sample actions

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action 2",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "users": [
            {
              "name": "martin",
              "department": "Success",
              "location": "Dublin"
            },
            {
              "name": "bobby",
              "department": "Support",
              "location": "Utah"
            },
            {
              "name": "thomas",
              "department": "Success",
              "location": "Dublin"
            },
            {
              "name": "nick",
              "department": "Success",
              "location": "North Carolina"
            },
            {
              "name": "kelli",
              "department": "Marketing",
              "location": "Boston"
            }
          ]
        }
      },
      "position": {
        "x": 30,
        "y": 1290
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "WHERE",
      "description": "You can read this: WHERE(my_action_2.users, 'location', my_action_1.location) as:\n\nGive me the result where My Action 2 has a location that matches My Action 1's location value",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "Where": "=WHERE(my_action_2.users, 'location', my_action_1.location)"
        }
      },
      "position": {
        "x": 30,
        "y": 1380
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "My Action 1",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "location": "Utah",
          "field_name": "location"
        }
      },
      "position": {
        "x": 30,
        "y": 1200
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    },
    {
      "sourceIdentifier": 2,
      "receiverIdentifier": 0
    }
  ],
  "diagramNotes": []
}
```

#### WORKBENCH_LINK

```
WORKBENCH_LINK()
```

Produces a URL that when visited will load the incoming event data in Workbench for a user, so long as they have permission to see the event.

**Categories:** Story Metadata

#### XML_PARSE

```
XML_PARSE(text)
```

Takes text containing XML and parses to an object

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": "<message>\n<to>World</to>\n<from>Tines</from>\n<body>Hello World!</body>\n</message>"
}
```

Formula:

```
XML_PARSE(my_action)
```

Output:

```json
{
  "message": {
    "body": "Hello World!",
    "from": "Tines",
    "to": "World"
  }
}
```

## Sample actions

```json
{
  "standardLibVersion": "13",
  "actionRuntimeVersion": "1",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": "<message>\n<to>World</to>\n<from>Tines</from>\n<body>Hello World!</body>\n</message>"
      },
      "position": {
        "x": 1470,
        "y": 3390
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "XML_PARSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "xml_parse": "=XML_PARSE(my_action)"
        }
      },
      "position": {
        "x": 1470,
        "y": 3495
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### YAML_PARSE

```
YAML_PARSE(text, [parse_date_or_time: FALSE], [version: 1])
```

Takes text containing YAML and parses to an object

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Input:

```json
{
  "my_action": "---\ntimeout: 10\nqueues:\n- one\n- two\n- three\nscheduler:\n  max_workers: 10\n"
}
```

Formula:

```
YAML_PARSE(my_action)
```

Output:

```json
{
  "queues": [
    "one",
    "two",
    "three"
  ],
  "scheduler": {
    "max_workers": 10
  },
  "timeout": 10
}
```

### Example 2

Input:

```json
{
  "my_action": "\nLinkedDate: 2007-04-01 12:00:00\n"
}
```

Formula:

```
YAML_PARSE(my_action, parse_date_or_time: TRUE)
```

Output:

```json
{
  "LinkedDate": "2007-04-01 12:00:00 UTC"
}
```

### Example 3

Input:

```json
{
  "my_action": "---\nNestedObject:\n  LinkedDate: 2007-04-01 12:00:00\n  List:\n  - :foo\n  - bar\n"
}
```

Formula:

```
YAML_PARSE(my_action, version: 2)
```

Output:

```json
{
  "NestedObject": {
    "LinkedDate": "2007-04-01 12:00:00 UTC",
    "List": [
      "foo",
      "bar"
    ]
  }
}
```

## Sample actions

```json
{
  "standardLibVersion": "15",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": "---\ntimeout: 10\nqueues:\n- one\n- two\n- three\nscheduler:\n  max_workers: 10\n"
      },
      "position": {
        "x": -1410,
        "y": 150
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "YAML_PARSE",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "yaml_parse": "=YAML_PARSE(my_action)"
        }
      },
      "position": {
        "x": -1410,
        "y": 255
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### YEAR

```
YEAR(date)
```

Returns the year for the specified date.

**Categories:** Dates/Times

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "message": "2022-03-19T17:49:01+0000"
  }
}
```

Formula:

```
YEAR(my_action.message)
```

Output:

```json
"2022"
```

## Sample actions

```json
{
  "standardLibVersion": "19",
  "actionRuntimeVersion": "4",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "message": "2022-03-19T17:49:01+0000"
        }
      },
      "position": {
        "x": 1035,
        "y": 1080
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    },
    {
      "disabled": false,
      "name": "YEAR",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "year": "=YEAR(my_action.message)"
        }
      },
      "position": {
        "x": 1035,
        "y": 1185
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null,
      "cardIconName": null,
      "createdFromTemplateGuid": null,
      "createdFromTemplateVersion": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### ZIP

```
ZIP(fileContentsOrFilesArray, [filename], [password])
```

Creates a ZIP archive containing a given file or files.

If the optional password parameter is present then the archive will be password protected. 

 The function can also accept an array of file objects as its first argument. The file object must contain either contents or base64encoded contents, a file name and optionally can specify the created_at date in ISO8601 format. 

 If the output of the ZIP function is being included in event data, it should be wrapped in the BASE64_ENCODE function to avoid errors - e.g. `BASE64_ENCODE(ZIP(my_action))`.

**Categories:** Data Parsing/Conversion

## Examples

### Example 1

Formula:

```
ZIP(file_contents, "file_name", "optional_password")
```

### Example 2

Formula:

```
ZIP(file_contents_1, "file_name_1", file_contents_2, "file_name_2", "optional_password")
```

### Example 3

Input:

```json
{
  "zip_files": [
    {
      "contents": "file_contents1",
      "created_at": "2023-01-11T15:06:18+00:00",
      "name": "file1.txt"
    },
    {
      "base64encodedcontents": "ZmlsZV9jb250ZW50czI=",
      "created_at": "2023-01-11T16:06:18+00:00",
      "name": "file2.txt"
    }
  ]
}
```

Formula:

```
ZIP(zip_files, "optional_password")
```

## Sample actions

### Sample action 1

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "New Page",
      "description": null,
      "options": {
        "fields": "",
        "mode": "success_page",
        "submissionMessage": "Thank you for your submission",
        "visibility": "tenant",
        "page_logo": {
          "contents": "iVBORw0KGgoAAAANSUhEUgAAAFQAAAAYCAYAAABk8drWAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAARCSURBVHgB7VhLctpAEB1k7LVyA/kEgRMEn8BQ5e8qcALjE1icAPsEkJW/VeATwA1QTmDlBCE7l3/kPblbTIiMJcVOeaFXBRrN9Mz0vOnu6ZExBQoUKFCgwAdByX4ZDAbuw8PDwWw2a+LVk+rAcZxgZWWl02g0QpMSl5eXXRlnvLq62kLfqcmIi4uLZqlU+lwulztJ/aGvJ/p+39nZ6cu8R3j30c/f3t7umP8MRwtXV1df7+/vb6iMmZNJVJ6enppom5ydnbVNCpAIjENZF786Fn1kMgLE1PDocRz0HyTJQKe2zNOjMbBO3o1sZgS2Ybw29KqYd0ZEKMg8AGl980zAS3BhqV0Sv0Qmsho8/iCQi5T6XED/WtJmoj7W9/b2Vgk9gXUGWM+JtoF4H/VdFEdK/HvB4UIxuZ+2A2SPlykFayLhXkJ9z/wDsJlHaTZld3fXR4ho7O3tHWudRbyrxC+Ca+L4rxFuyXlJ7c7j4yOtKcuuuXd3d+0XJvMkZPwFWpm4cV64sLTBawuGB/UYus7Pz7vUB3NOYLGxV+EsuIHrT2ydITNCn5/sxyfaKdO0x02Qo8wNY7Yt52ChmeOKraAN2ZxlyBxLZb6xFCuvxWN4UE361PGYYn1egli0KSQJxIy42VIfso959rCehplFOYYUkY0MCJvn68C5CDUJLi2ho7msExVa3Pk0QL9veJxIuZ3W0pkZIMNYR3GodSBjA3VVlsUAPK1HprCOuqoQpmHGRV3FzNfcQvZQpSx06YjcFx3fMW8E7mBK0W6egwEk+ObZKkjqIO0hR1Ih/0vfYeGhpmAwgLpUX4OkMQv7+/shCNV0i+GtBrlYX5Kn2QLjNTeCMVvby6KkZ7IhsF/E6jyTDhqDfZMBJAHzUHHGP1cOuR8mJ05PTz0zPzs2MfZM20BgLAfCXGzmEAYTWbN4YRPy3JQxNusauo1V3hF3yopg4T1TbISSB3nSKLhZgL6HLDN8oLxp3ga8vPSTfnD3MTcTzw1ypeHASI6NX88+5Jy1tTWmF1luMRw8voHIKeeZbHBTHGCJgGseW4dU7pySrm3m6w62trZa9o/kQcdrkTNwaw9c8fZVhcV+oqtb5FY0DDgSTw7TKgKT7+gkhH0jyQK6Tt4kG+7eMtmMIAasLj6ELULqtsfw8oJ1jdA+YGjgLYvvPCcoR84Yc63+UWiIxucf78HiSsuUZFvLTpgF73rzSAI3FMS0MnQJtUCSmD/KdwvdGFfyygnb5NZIBGI8ShzTpwnzUcpaWU2oh1p8ytOVmDIsxIlImOkB0w/9AGFDFhaabJhCmcNlH0zgYoGxXHKxHW451LSF86vXQPdQ6sYqy7BmhYlofs7NPpImaZumR5y3jzVvsFLI0nW6ko9WRG6oaVg0v/nA0JCwjHhx1aktw3jGA+yF8dykr2ZisRVsZLg4XpIcSGWOG+b5ilagQIECBZLxG5sJ41WbSDouAAAAAElFTkSuQmCC\n",
          "display": false,
          "name": "nites.jpeg",
          "type": "image/jpeg"
        },
        "page_width": "small",
        "pages_action_color": "#777FFF",
        "pages_appearance_mode": "light",
        "pages_background_color": "#F9F9F9"
      },
      "position": {
        "x": 1785,
        "y": 5685
      },
      "type": "form",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": {
        "fields": [
          {
            "content": null,
            "defaultValue": null,
            "description": "",
            "maxCharacters": null,
            "multiSelect": false,
            "name": "File upload 1",
            "options": [
              "Option 1",
              "Option 2"
            ],
            "required": false,
            "type": "FILE_UPLOAD",
            "validationPattern": null
          },
          {
            "content": "Submit",
            "defaultValue": null,
            "description": "",
            "maxCharacters": null,
            "multiSelect": false,
            "name": "Button",
            "options": [
              "Option 1",
              "Option 2"
            ],
            "required": false,
            "type": "BUTTON",
            "validationPattern": null
          }
        ]
      }
    },
    {
      "disabled": false,
      "name": "ZIP",
      "description": "",
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "zip": "=BASE64_ENCODE(ZIP(BASE64_DECODE(new_page.body.file_upload_1.contents)))"
        }
      },
      "position": {
        "x": 1785,
        "y": 5985
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

### Sample action 2

```json
{
  "standardLibVersion": "14",
  "actionRuntimeVersion": "3",
  "agents": [
    {
      "disabled": false,
      "name": "My Action",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": [
          {
            "contents": "file_contents1",
            "name": "file1.txt",
            "created_at": "2023-01-11T15:06:18+00:00"
          },
          {
            "base64encodedcontents": "ZmlsZV9jb250ZW50czI=",
            "name": "file2.txt",
            "created_at": "2023-01-11T16:06:18+00:00"
          }
        ]
      },
      "position": {
        "x": 2100,
        "y": 5880
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    },
    {
      "disabled": false,
      "name": "ZIP",
      "description": null,
      "options": {
        "mode": "message_only",
        "loop": false,
        "payload": {
          "zip": "=BASE64_ENCODE(ZIP(my_action))"
        }
      },
      "position": {
        "x": 2100,
        "y": 5985
      },
      "type": "eventTransformation",
      "timeSavedUnit": "minutes",
      "timeSavedValue": 0,
      "monitorAllEvents": false,
      "monitorFailures": false,
      "monitorNoEventsEmitted": null,
      "form": null
    }
  ],
  "links": [
    {
      "sourceIdentifier": 0,
      "receiverIdentifier": 1
    }
  ],
  "diagramNotes": []
}
```

#### ZSCALER_OBFUSCATE_API_KEY

```
ZSCALER_OBFUSCATE_API_KEY(api_key, now)
```

Obfuscates a Zscaler API key using the function described in the [Zscaler API documentation](https://help.zscaler.com/zia/getting-started-zia-api#CreateSession). For the "now" argument provide the current time in milliseconds since the epoch with DATE(NOW(), "%s%L").

**Categories:** Hashing/Signing

## Examples

### Example 1

Formula:

```
ZSCALER_OBFUSCATE_API_KEY("my_api_key", DATE(NOW(), "%s%L"))
```

Output:

```json
"yy_k_p_ip"
```

### Operators

#### `a = b`

'Equal to' comparison. Returns `TRUE` or `FALSE`

#### `a != b`

'Not equal to' comparison. Returns `TRUE` or `FALSE`

#### `a > b`

'Greater than' comparison. Returns `TRUE` or `FALSE`

#### `a >= b`

'Greater than or equal to' comparison. Returns `TRUE` or `FALSE`

#### `a < b`

'Less than' comparison. Returns `TRUE` or `FALSE`

#### `a <= b`

'Less than or equal to' comparison. Returns `TRUE` or `FALSE`

#### `a && b` (alternatively `a AND b`)

'Logical and' comparison. Returns `TRUE` or `FALSE`

#### `a || b` (alternatively `a OR b`)

'Logical or' comparison. Returns `TRUE` or `FALSE`

#### `a * b`

Multiplication. Returns product of `a` and `b`

#### `a / b`

Division. Returns `a` divided by `b`

#### `a + b`

Addition. Returns sum of `a` and `b`

#### `a - b`

Subtraction. Returns `a` minus `b`

#### `a & b`

Concatenation. Returns a single text value, the text from `a` followed by the text from `b`

#### `func1() |> func2(%)`

Pipeline operator. Allows you to avoid nesting of function calls. The output of the function on the left is available as `%` in the right hand side.

### Prompts

Prompts can be used to create a feedback loop from Tines to the end-user. The formula function `PROMPT` can be included in any action, and when the URL created by the prompt is visited by a user,  the action that implements the prompt will emit a new event. 

Documentation on the usage of the `PROMPT` function can be found [here](https://www.tines.com/docs/formulas/functions/prompt).

## Example Configuration

Email a prompt URL to an address using the Email action:

```json
{
  "recipients": [
    "alice@example.com"
  ],
  "sender_name": "Alice",
  "subject": "Visit this link",
  "body": "<<PROMPT(\"OK\")>>",
  "advanced_html": true
}
```

When the action is run, the inbox listed in the recipients field, will receive an email with the below contents:

![](https://www.datocms-assets.com/55802/1718378638-screenshot-2024-06-14-at-16-23-48.png)

When the link in the email is clicked, an event will be emitted by the email action specified above, with the payload:

```json
{
  "send_email_action": {
    "prompt": {
      "agent_id": <action-id>,
      "event_id": null,
      "status": "OK"
    }
  }
}
```

The text passed into the `PROMPT` function will determine the value of the status key in the emitted event of the action.

### Language description

## Types

The formula language has the following types, which correspond to the [types](https://en.wikipedia.org/wiki/JSON#Data_types) in standard JSON:

### Text

To enter text, you can wrap your values in either single or double quotes

```python
# Double quotes
"this is some text"

# Single quotes
'this is also some text'
```

There is no difference in behavior between the quote styles.

If you need to use a quote inside some text you can escape it with `\`

```python
# Escaping text
"As Einstein said \"Never memorize something that you can look up.\""
```

### TRUE/FALSE

Booleans can be accessed like so, note that case is significant:

```python
# true
TRUE

# false
FALSE
```

Formulas does not support type coercing, ie. `[]` will not be coerced to `FALSE`. All values are truthy by default except for `FALSE` & `NULL`.

### NULL

Null values are written with all caps and are falsy:

```python
NULL

# true
IS_BLANK(NULL)
```

### Numbers

Numbers are written in simple notation.

```python
# number
123

# decimal
123.45

# negative
-123
```

### Arrays

Usually you will interact with arrays that come from your event data, however you can also create an array either using the array function or using array literal syntax.

```python
# creating an array using a function
ARRAY(1, 2, 3)
# [1, 2, 3]

# creating an array using array literal
[1, 2, 3]
# [1, 2, 3]

```

To access an item in an array, you use square brackets and numbers, the first item is at position zero:

```python
# my_array =  ["first", "second", "third"]

my_array[0]

# "first"
```

### Objects

Usually you will interact with object that come from your event data, however you can also create an object using the object function or object literal syntax.

```python
# creating an object using function
OBJECT("key1", "value1", "key2", "value2")
# {key1: "value1", key2: "value2"}

{key1: "value1", key2: "value2"}
# {key1: "value1", key2: "value2"}
```

When using object literal syntax, quotes are not required around keys unless needed (e..g if you want a key with a space in it).

## Operators

Operators allow you to compare values or perform basic math on them:

```python
# check if equal
a = b

# check if greater than
body.count > 1

# multiply
body.count * 5
```

See the reference section for more details.

## Functions

Functions allow you to perform operations on event data.

You call a function by using its name followed by opening and closing parentheses.

All functions are written in uppercase.

```python
MY_FUNCTION()
```

Most functions will accept one or more arguments, which can be specified like this:

```python
MY_FUNCTION(argument1, argument2)
```

Function calls can be nested:

```python
MY_FUNCTION(FUNCTION1(), FUNCTION2())
```

### Function chaining

Since function nesting can result in difficult to read expressions, we also support the ability to chain function calls.

Calls are chained like so:

```python
REPLACE(my_event.url, "https", "http[s]") |> UPCASE(%)
```

The result of the function on the left is made available on the right as `%`, where it can be used in any place

### Lazy evaluation

Arguments to most functions will be eagerly evaluated, for example take the following nested functions:

```python
MY_FUNCTION(FUNCTION1(), FUNCTION2())
```

When this is evaluated, `FUNCTION1` will be called first, then `FUNCTION2`, finally `MY_FUNCTION` will be called with the results of the previous two calls passed as arguments.

Exceptions to this rule are `IF` `AND` and `OR`, in these functions arguments are lazily evaluated, this allows you to effectively use these to control execution:

```python
IF(FUNCTION1(), FUNCTION2())
```

Here `FUNCTION2` will only be called if the `FUNCTION1` returns true.

## Lambdas

Lambdas are a custom, reusable function that you create using the `LAMBDA` function. This function is a little different from other functions because you specify the placeholders as the arguments to the function.

```python
LAMBDA(a, b, a + b)
```

Here `a` and `b` are the arguments that will need to be passed when the lambda is called and the `a + b` is the expression that will be evaluated when the lambda is called.

There are three ways of using lambdas.

### Immediately invoked

First they can be immediately invoked i.e. `LAMBDA(a, b, a + b)(1, 2)`. This will create a function to add two numbers together and then immediately call it. This might not seem very useful, but it can help avoid repetition in cases where you need to re-use the same calculation in multiple places.

For example if you wanted to check if the current time is between 9 and 5 you might do something like this:

```python
IF(
  AND(
    DATE("now", "%H") >= 9,
    DATE("now", "%H") < 17,
  ),
  "office hours",
  "after hours"
)
```

Using a lambda you could avoid this repetition like so:

```python
LAMBDA(
  current_hour,
  IF(
    AND(
      current_hour >= 9,
      current_hour < 17,
    ),
    "office hours",
    "after hours"
  ),
)(DATE("now", "%H"))
```

### With an Array function

The next way you can use Lambdas is as an argument to the functions `MAP_LAMBDA`, `FILTER`, `FIND` and `REDUCE`. For example let's imagine we have the following array of data in a field called `fruit`:

```python
[
  {
    "name": "apple",
    "in_stock": 0
  },
  {
    "name": "banana",
    "in_stock": 5
  },
  {
    "name": "pear",
    "in_stock": 6
  }
]
```

If we wanted to find all the items that are in stock we could write the following:

```python
FILTER(fruit, LAMBDA(item, item.in_stock > 0))
```

Or if we wanted to find the item with the name `pear` we could do

```python
FIND(fruit, LAMBDA(item, item.name = "pear"))
```

Or if we wanted to extract all the names we could do this:

```python
MAP_LAMBDA(fruit, LAMBDA(item, item.name))
```

### As a LOCAL or RESOURCE

The final way you can use lambdas is to assign them to LOCAL or to a RESOURCE and then call them from somewhere else. This can be a great way of defining your own re-usable bits of functionality.

![](https://www.datocms-assets.com/55802/1655989698-formulas_lambda_in_resource-ccb1924cb0955f74abcbe3d70a221daf.png)

Here we have created a lambda for defanging URLs and stored it in a `RESOURCE`. The lambda looks like this:

```
LAMBDA(
  url,
  url
    |> REPLACE(%, ".", "[.]")
    |> REPLACE(%, "http", "hxxp")
)
```

Ensure the lambda in the Resource, is defined inside of single value mode:

![](https://www.datocms-assets.com/55802/1656586401-single_value_mode_lambda_in_resource-4c580d87695c0ba25ff525c6780a423c.png)

*Lambda in a resource*

With this in place we can use it from any story like so:

![](https://www.datocms-assets.com/55802/1655989711-formulas_call_lambda_in_resource-3ad244ca2079cbea224c9356e6d19f68.png)

```python
RESOURCE.my_lambdas.defang(url)
```

## Tags

Tags are only available in text mode. You can nest tags inside each other. There must always be a corresponding `end` tag for each tag.

### if/elseif/else/endif

These tags all work together to allow you to conditionally evaluate sections in text mode.

At its simplest you can have an `if` and `endif` pair:

![](https://www.datocms-assets.com/55802/1655989723-actions_formulas_if_tag_1-d6b518c39baed0b139ab1a1cbb59de2e.png)

If this is run with a user named Alice, it will output `Hi Alice`, but if the user has no name it will just output `Hi`.

You can also add an `else` block to act as a catch all:

![](https://www.datocms-assets.com/55802/1655989737-actions_formulas_if_tag_2-e3dd0cd6e35a057686a0f4bab231ac35.png)

Now if the user has no name it will output `Hi there`.

Finally you can add more conditions using `elseif`:

![](https://www.datocms-assets.com/55802/1655989779-actions_formulas_if_tag_3-1833ef88fb86c3fc19ac98926ce0c8c1.png)

You can add as many additional conditions with `elseif` as you like.

As mentioned above, Formulas does not coerce types, with all but `NULL` & `FALSE` being truthy. To check if a value is blank simply write the following:

![](https://www.datocms-assets.com/55802/1655989788-actions_formulas_if_tag_4-da94aa59015808ccd59d1b57749b9dec.png)

### for/endfor

The `for` tag allows you to repeat the same block of code multiple times for each item in an array.

![](https://www.datocms-assets.com/55802/1713172457-screenshot-2024-04-15-at-10-14-00.png)

This will output `User names: `followed by the names of all the users in the array `users`.

Within a for tag, there is a special `FORLOOP` variable available with the following properties:

`FORLOOP.index0`: The zero based index of the current loop iteration. That is the first time through the loop this will be 0, the second time 1 and so on.

`FORLOOP.index`: The one based index of the current loop iteration. That is the first time through the loop this will be 1, the second time 2 and so on.

`FORLOOP.first`: This will be true the first time through the loop.

`FORLOOP.last`: This will be true the last time through the loop.

For example, if we wanted to output a comma between every name we could do something like this:

![](https://www.datocms-assets.com/55802/1655989812-actions_formulas_for_tag_2-042ce7553e1950b879e98c4a7f8eff17.png)

### raw/endraw

The `raw` tag allows you to escape any content within it, preventing interpolation at runtime. This escaping allows for repeated `<` to be expressed within formulas without triggering interpolation.

![](https://www.datocms-assets.com/55802/1713172484-screenshot-2024-04-15-at-09-58-30.png)

## Pages

Pages allow you to expose simple, beautiful web pages connected to your Stories.

Through these web pages, your end users can provide input to – and view output from – your Tines-powered workflows.

Use pages to do things like:

- [**Kick off a workflow**](https://www.tines.com/docs/collecting-input-with-pages/) with user input in forms
- **Get detailed human input** mid-way through a Story run
- **Build requester/approver flows** for internal tools, with great UX

By combining flows of multiple pages and [actions](https://www.tines.com/docs/actions/) together, you can even [build end-to-end apps with Tines](https://www.tines.com/docs/building-apps-and-flows/).

> **NOTE:** Pages are available on version 14.02 of self-hosted Tines.

## Adding a page

Drag a page onto the diagram from the ‘Tools’ menu. Then, connect it to a downstream action, to receive submissions for any user input.

**

## Authoring pages

To build and customize your page, double-click to open it. This will display the templates page. Once a template has been selected, you'll see the page editor, where you can add and customize [page elements](https://www.tines.com/docs/list-of-page-elements/).

![](https://www.datocms-assets.com/55802/1770312439-screenshot-2026-02-06-at-01-26-36.png)

### Collecting input with pages

The most common way to use pages is to kick off a Story run based on some structured user input.

## Defining input fields

First, [add a page](/docs/pages#adding-a-page) and double click it to open the editor.

Then, use the page editor to select form input fields for the type of information you want to collect from the user. We support a variety of configurable inputs, from **email address** to **file upload**.

![](https://www.datocms-assets.com/55802/1770312577-screenshot-2026-02-06-at-01-28-51.png)

Each input field can be individually customized – e.g. to mark as required, set validation criteria like maximum length, add a contextual description, or show/hide conditionally.

## Connecting to your story

Once your page is configured to accept the input you need, it's time to connect it to downstream actions (or other pages).

Just like actions, pages produce [events](/docs/events) once they're submitted, and these events get passed to everything downstream, providing contextual data.

For a simple example, let's say we've connected our page to an action that sends an email:

![](https://www.datocms-assets.com/55802/1670451252-cleanshot-2022-12-07-at-16-59-01-2x.png)

The "Send an email" action could then access the submitted page data using a [formulas expression](/docs/formulas/referencing-data) like the following:

```
new_page.body.example_input
```

### Page event

Sample page event with all possible input fields submitted:

```json
{
  "page_action": {
    "body": {
      "button": "Submit",
      "short_text": "Short text",
      "long_text": "Long text",
      "email": "support@tines.io",
      "web_url": "https://tines.com",
      "option": ["Option 1"],
      "date_or_time": "2025-05-29 12:00:00",
      "boolean": true,
      "number": 1,
      "slider": 11,
      "file_upload": {
        "name": "image.png",
        "type": "image/png",
        "contents": "redacted base64 encoded representation of the image",
        "display_logo": true
      },
      "password": "redacted string"
    },
    "headers": {
      "x_forwarded_for": "192.0.2.1",
      "x_forwarded_proto": "https",
      "x_forwarded_port": "443",
      "host": "your-tenant.tines.com",
      "x_amzn_trace_id": "Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx",
      "sec_ch_ua_platform": "\"macOS\"",
      "sec_ch_ua": "\"Not.A/Brand\";v=\"99\", \"Chromium\";v=\"136\"",
      "sec_ch_ua_mobile": "?0",
      "baggage": "sentry-environment=production,sentry-release=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,sentry-public_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,sentry-trace_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,sentry-org_id=000000,sentry-sampled=false,sentry-sample_rand=0.7173583071078177,sentry-sample_rate=0.2",
      "sentry_trace": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx-0",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
      "accept": "application/json",
      "x_tines_client_build_version": "xxxxxxxxxx",
      "origin": "https://your-tenant.tines.com",
      "sec_fetch_site": "same-origin",
      "sec_fetch_mode": "cors",
      "sec_fetch_dest": "empty",
      "referer": "https://your-tenant.tines.com/pages/url-identifier/",
      "accept_encoding": "gzip, deflate, br, zstd",
      "accept_language": "en-US,en;q=0.9",
      "priority": "u=1, i",
      "version": "HTTP/1.1",
      "content-type": "application/json",
      "content-length": "3501853",
      "request-method": "POST",
      "email": "your@email.com",
      "date": "Wed, 28 May 2025 17:58:34 UTC",
      "request_ip": "192.0.2.1"
    }
  }
}

```

### Anonymized page event

When collecting feedback or survey responses in Tines Pages, you can enable anonymous submissions to protect user privacy. This feature removes identifying information such as IP address, email address, and browser details from page submissions.

**

By default, page submissions include user identifying information in the event headers. When anonymization is enabled, user-identifying headers are not included in the page event.

```json
{
  "page_action": {
    "body": {
      "button": "Submit"
    },
    "headers": {}
  }
}
```

You can toggle anonymize page submissions on the right panel on the Story editor. When users visit the page, they'll see "Your response will be submitted anonymously" at the bottom of the page.

The feature is only available if the header data is not referenced in downstream actions.

### URL query parameters

In addition to accepting inputs from users, input fields can be populated using URL query parameters. If a page has a Short text field with the name "name" and an Option field with the name "job title" and values "Software Engineer", "Designer"; the page URL `https://sample-tenant-1357.tines.com/pages/sample-guid?name=alice&job_title=Software%20Engineer` when visited will populate the name field with the input "alice" and option field with the input "Software Engineer".

When providing values for fields, the slugified name of the field should be used as the query parameter name. Query parameters are supported for the input fields: Short Text, Long Text, Email, URL, Option, Date or time, Boolean and Number. 
The Date or time query parameter accepts inputs using the ISO format. All other input fields accept text.

### Building apps and flows

By connecting an arbitrary number of pages and actions, it's possible to build complex flows, and even self-contained applications, right here in Tines.

## Connecting pages and actions

Pages and actions can be connected in arbitrary complex ways, to build rich user experiences.

Both actions and pages are represented on the Story diagram. You can think of the actions being the *back-end* of the app (the automation happening in the background) while the pages are the *front-end *(what the user sees).

![](https://www.datocms-assets.com/55802/1670464013-cleanshot-2022-12-07-at-20-33-19-2x.png)

*Actions and pages connected to form an app. You can see both the ‘front-end’ and the ‘back-end’ on the Story diagram.*

## Controlling transitions between pages

You can customize a page's submission mode in the right-hand-side configuration sidebar when it's selected on the diagram. Pages have three different submission modes to choose from:

- `Show success message` – immediately bring the user to a success page after submission, disabling transition to subsequent pages
- `Move to next page` – seamlessly transition to the next page once ready, with a loading state in between
- `Redirect to URL` – transition to a custom URL immediately after submission

**

For the most secure starting point, each page defaults to `Show success message` behavior.

However, the vast majority of the time, when building apps or flows with multiple pages, you'll want to select `Move to next page` to allow for automatic page transitions.



## Displaying dynamic data on pages

All [page elements](/docs/pages/list-of-page-elements) support [formulas](/docs/formulas).

For input fields, formulas can be generally used as default values. Additionally, in the case of option fields, the option list can be powered by an array-producing formula.

For display fields, like paragraphs, headings, and images, formulas can be used to provide displayed content.

When a page renders, it has access to the full execution context – just like actions. That means you can reference any piece of upstream data encountered during the Story run. For example, if you had created a Jira issue, you could fetch and include its ticket number on a confirmation screen.

### Default values for elements

Please note for default values on elements you can generally use formulas for all fields. The fields will update based off the default value set until you interact with the element. 

This especially comes in handy because short/long text/option fields now support referencing other elements with `META.page_elements.<element>` in their `default value`. So you can update a short text field based on other elements on the page, but once a user types into the field, it will maintain that value and no longer update automatically. Until then, any updates to the referenced element will execute and propagate down to the text element's default value.

### Page templates

Page templates give you a starting point to build from when creating pages. When you add a page to your story and click ‘Edit’, you can choose to start with a blank page, or from one of the preconfigured templates.

**

> **NOTE:** Templates are intended as a starting point. Make sure you review and customize all options before publishing your page.



You can choose from the following templates:

| Template | Description |
| --- | --- |
| Allowlist | A form that allows users to allow specific items, users, or entries. |
| App / service request | An app for users to request access to apps and services. |
| Basic form | A simple form with a selection of user inputs for submission. |
| Blocklist | A form that allows users to block specific items, users, or entries. |
| Bug / issue report | 
Form to report issues and bugs.

 |
| Expense reimbursement | Form for an example of expense reimbursements. |
| Hardware request | 

Form for requesting hardware.

 |
| 

New employee form

 | Example form for new employee information. |
| Product feedback form | An example form for feedback on your product. Here you’ll also find examples of conditions. |
| Simple survey | Example of a survey form. |

## **Adding a template to your story**

You can browse through templates on the left, and the page preview will update when you select a new template. This will show you what the end user experience will look like. To add a page to your story and enter the page editor, hit ‘Select’.

![](https://www.datocms-assets.com/55802/1748342059-template-select.png)

Page templates include various elements like text, controls, input fields, containers and conditional fields. Selecting these elements on the page will open the editing options for these on the right. You can learn more about these elements [here](https://www.tines.com/docs/pages/list-of-page-elements/).

**

If you’ve selected a template and want to change to another, return to your storyboard and delete it. You can then add a new page and select a new template.

### Branding and style

Because Tines pages are typically [distributed to end users who may not be members of the Tines tenant](https://www.tines.com/docs/distribution-and-access-control/), it's important for them to look and feel like your organization.

## Customizing the page style

Use the controls on the right of the page editor to style the page.

You can apply a custom:

- **Logo**, which will appear at the top of the page.
- **Background color,** which will display behind the page.
- **Action color**, which will be used to tint certain interactive elements like buttons.

You can also choose whether the page appears in light mode or dark mode and define page width.

[](https://demo.arcade.software/y10R8DNS1XwJbIfP2yJ4?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

### Layout options

Choose between two layout styles for your page:

- **Framed**: Displays your page content within a centered container with a maximum width, creating margins on larger screens
- **Unframed**: Allows your page content to span the full width of the browser window, without the background color or centered container. It also adds a header to the page to allow users to toggle between light and dark mode based on their own preference

## Consistent styling across pages with themes

Because some complicated workflows may display different pages to different audiences, we allow for each page having its own unique style and brand. To share branding between different pages you can use **Page themes**.

**

In the **Page theme** section  you can save the branding and style of the current page as a named page theme. Then you can use the same section on another page to apply that page theme. 

> **TIP:** When creating a template, you can choose whether it's available to this team only, or all teams in your tenant.

### Layout and containers

## Containers

You can group multiple page elements together into a container. Containers allow you to logically group together elements on a page. You can apply conditions and layout options to a container. Conditions and layout options will affect every element inside the container. 

### Creating a container

You can create a container by dragging elements on top of each other or by creating copies of an element. When you create a copy of an element, the new element will automatically be added to a container along with the original element. 

[](https://demo.arcade.software/fENacVCXAFhVGRcfODZg?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

### Applying conditions to a container

Once you have grouped elements together into a container, you can click on the container and use the options in the right hand panel to configure it. To apply [conditions](https://www.tines.com/docs/conditional-page-elements/) to the container, click on the conditions tab in the right panel and then use the form to configure your conditions. Once conditions have been added an indicator will be added to the container to show that the container is controlled by a condition. Conditions applied to the container will impact all elements within the container. 

*Adding conditions to a container*

### Copy a container

You can copy a container which will create a new container with all of the same elements, conditions, and layout options applied. This can be useful when you want to quickly build similar groups of content.  

*Copy a container*

### Add a name to a container

You can name your containers to make it easier to remember what each container represents. This will be helpful when you come back to edit the page or if multiple people are working on a page together. 

*Add a name to a container*

### Customise the layout for a container

Containers have `layout` and `alignment` options which you can use to customise the display of elements within the container. Elements can be laid out either horizontally or vertically and can be aligned to either the start, center, or end of the container. 

When elements are added horizontally you can add as many elements as you like and each will receive it's own column inside the container. 

*Customise the layout of a container*

### Limits

For [looping containers](https://www.tines.com/docs/looping-page-elements/), there is a limit of 100 total looped elements in the container across all loop iterations. 

For regular containers, there is no hard limit on the number of elements that can be added. Note that containers with many elements may impact page rendering performance.

### Distribution and access control

Pages are designed to serve audiences beyond your team members already on Tines.

To get pages into the hands of these end users, you'll need to distribute them while ensuring secure access controls are established.

## Distributing page URLs to users

### Pages which kick off story runs

For pages that kick off workflows – i.e. pages at the root of a story without anything else upstream – a static URL is provided.

Use the link icon in the top right of the page on the diagram to copy its URL (or click to open in a new tab).

![](https://www.datocms-assets.com/55802/1670448094-cleanshot-2022-12-07-at-16-20-48-2x.png)

### Downstream (mid-story) pages

Pages downstream of actions or other pages do not have static URLs. This is because they exist in the context of a particular story run.

To get the dynamic URL for these pages, you can use the `PAGE.page_name` variable in Formulas expressions. This produces a link to the page, ready for sharing, in the context of the current story run.

For example, you could add the page downstream of a [Send Email Action](/docs/actions/types/email), and include a link to the page in a Slack notification. Once the user accesses the link and submits the form on the page, the workflow will continue as usual:

**

In this example, the page action is called "Collect input". The `PAGE.collect_input` formula expression will retrieve the dynamic URL of the page, allowing you to send it to the end user. When the user clicks on the page, the dynamic URL brings the user to the page with the appropriate story run context.

### Customizing the URL

A page URL looks like `https://tenant.tines.com/pages/url-identifier/dynamic-identifier`. By default, the `url-identifier` is a randomly generated string. It must be unique to the tenant to ensure that your users can find it.

You can provide your own url identifier to customize where your users can find it, e.g. `analyze-email`. Be cautious, for this may break existing bookmarks. If changing the url identifier of an existing page, consider using another page to redirect users from the old identifier to the new one.

The `dynamic-identifier` is generated in the context of a story run (i.e. downstream pages) and cannot be customized.

![](https://www.datocms-assets.com/55802/1704373095-screenshot-2024-01-04-at-12-57-44.png)

## Controlling end-user access

By default, pages can only be loaded by those currently signed into the Tines tenant.

In total, there are four levels of access control that you can apply to each page:

1. **Only team members (default):** only signed-in members of the [team](https://www.tines.com/docs/admin/teams) that the page is in can load the page
2. **Members of this Tines tenant**: anyone signed into the Tines tenant can load the page (even if they’re not a member of the page's own team).
3. **Anyone with the link:** anyone with knowledge of the page URL can load and interact with pages.
4. **Via SSO authentication:** anyone in your organization granted access to the Tines app on the [SSO provider](https://www.tines.com/docs/single-sign-on/) can load the page. Useful for company-wide access scenarios. It's possible to add a further layer of granularity by restricting access to a particular group:
  
  - Firstly, an admin needs to [turn this feature on in the Authentication settings](/docs/admin/single-sign-on#sso-group-based-page-access)
  - Then select **Via SSO Authentication** under the access options of your page, enable **Restrict to specific SSO groups** and type a comma-separated list of SSO group names into the field below.

Customize your page's access by selecting it and scrolling to the bottom of the configuration sidebar.

![](https://www.datocms-assets.com/55802/1704373461-screenshot-2024-01-04-at-13-04-07.png)

### Restricting end-user access options

It is possible for tenant owners to restrict the use of the "Anyone with the link" access control option for specific roles: All roles, team admins and tenant owners only, or nobody. This means some or all users on your tenant can no longer enable "Anyone with the link" for their pages. 

To do so, visit your tenant's Setting center → Action settings → Page settings, then select which roles can use the "Anyone with the link" option.

**

Note: This restriction does not change existing pages retroactively. To review which pages are still public, tenant owners can visit the Settings center → [Page access](https://www.tines.com/docs/page-access/) page to update these pages access settings manually. 

## Embedding pages

 You can seamlessly integrate a Tines page in your own website by following these steps:

1. Ensure your Tines page's [access control setting](#controlling-end-user-access) is set to "Anyone with the link".
2. Add the following HTML to your website and replace the `src` with your Tines page URL:

```html
<iframe
  title="Tines page"
  src="https://tenant.tines.com/pages/url-identifier" 
  style="border:none; width: 100%; height: 100%;">
</iframe>
```

Learn more about [iframes on MDN web docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe).

### List of page elements

Pages support a range of elements, including input fields and display elements.

### Input fields

- **Short text** – a single line of text, e.g. "first name"
- **Long text **– multiline text, e.g. "address" or "comments"
- **Email** – an email address (with format validation)
- **Web URL** – a web address (with format validation)
- **Option** – one or more options selected from a predefined list
- **Date or time** – a date and/or time input with a user-friendly date picker
- **Boolean** – a yes/no response
- **Number** – a quantity
- **File upload** – a file attached from the user's computer (maximum of 20 MB for each page submission across all upload fields)
- **Password** – collect sensitive input from users, starring out as they type, with the option to encrypt input before storing. To avoid sensitive data appearing directly in event data, you can provide a formula expression that transforms the `password_value` the user enters, before it’s included in event data.
  
  - For anything consequential, we recommend encrypting the value – ideally using an asymmetric approach like `RSA_ENCRYPT` or `RSA_AES_HYBRID_ENCRYPT`. 
  - The expression should be wrapped in the `BASE64_ENCODE` function if the payload contains binary characters. `BASE64_DECODE` can be used later to decode the payload again. For example:

```
BASE64_ENCODE(RSA_ENCRYPT(password_value, CREDENTIAL.rsa_private_key))
```

### Display elements

- **Heading** – large text for titles
- **Rich text** – formatted body text. This supports formatting using the [Markdown markup language](https://www.markdownguide.org/basic-syntax/). We support the [CommonMark](https://commonmark.org/) specification.
- **Divider** – a horizontal rule to separate page sections
- **Button** – whenever you collect input on a page, you’ll need a button to allow the user to press submit and advance to the next page
  
  - Additionally you can give it a submission value to pass that custom value to the event, otherwise the label of the button will be send.

![](https://www.datocms-assets.com/55802/1724688279-screenshot-2024-08-26-at-17-04-24.png)

```
"body": {
  "button" : "custom value" // Submission value checked
},
```

```
"body": {
  "button" : "Submit" // Submission value unchecked
},
```

- **Image** – render an image by providing a direct upload (max 1024KiB), a URL, or a formula reference that contains an object with [Base64 encoded](https://www.tines.com/docs/formulas/functions/base64-encode/) contents, type, and name keys. This matches the object produced by the file upload element.
- **Map** - render a location on a map by providing a path to a an array of map marker objects. Each object should have a latitude and longitude, and can have an optional label also. Our [ARRAY](https://www.tines.com/docs/formulas/functions/array/) and [OBJECT](https://www.tines.com/docs/formulas/functions/object/) formulae may be helpful. As an example, 

```
ARRAY(OBJECT("latitude", 53.3857, "longitude", -6.2404, "label", "Ireland"))
```

will produce the following:

```json
[
  {
    "latitude": 53.3857,
    "longitude": -6.2404,
    "label": "Ireland",
  },
]
```

![](https://www.datocms-assets.com/55802/1743002916-screenshot-2025-03-26-at-15-26-55.png)

*Source: [Geo-lookup IP addresses in bulk (Tines Library)](https://www.tines.com/library/stories/1144115/?name=geo-lookup-ip-addresses-in-bulk)*

- **File** - share a file either via direct upload or dynamically using a formula reference. The latter must must evaluate to an object with contents ([Base64 encoded](https://www.tines.com/docs/formulas/functions/base64-encode/)), name, and type (the file’s content type) keys. This matches the object produced by the file upload element.
- **Table** - to render data from the events within a table by providing a path to a CSV parsed with [CSV_PARSE](https://www.tines.com/docs/formulas/functions/csv-parse/) or [CSV_PARSE_TO_OBJECTS](https://www.tines.com/docs/formulas/functions/csv-parse-to-objects/). The latter will render headings.
  
  - You can enable the `Allow row selection` option to make table rows selectable. Any selected rows will be submitted with the page data.
- **Chart** - to visualize data from events in a line, bar, or pie chart. 
  
  - Line and bar chart - provide a path to an array of objects containing graph points. Each object should have a name for the x-axis label, an amount for the x coordinate, and a value for the y coordinate. Example:

```
ARRAY(
  OBJECT("name", "Jan", "amt", "1","value", 10),
  OBJECT("name", "Feb", "amt", "4", "value", 20),
  OBJECT("name", "Mar", "amt", "8", "value", 15),
  OBJECT("name", "Apr", "amt", "10", "value", 30),
  OBJECT("name", "May", "amt", "21", "value", 25)
)
```

- Pie chart - Provide a path to an array of objects containing pie chart slices. Each object should have a name labelling the pie chart slice and a value indicating how much space it should take up. Example:

```
ARRAY(
  OBJECT("name", "Marketing", "value", 35),
  OBJECT("name", "Sales", "value", 28),
  OBJECT("name", "Engineering", "value", 42),
  OBJECT("name", "Operations", "value", 18),
  OBJECT("name", "Support", "value", 22)
)
```

### Conditional page elements

Conditions can be used to dynamically show, hide, or disable individual page elements. They can be configured to evaluate input values from other elements on the same page or can utilize formulas to access data from other sources such as upstream event data or resources.






[](https://demo.arcade.software/PhARdb8lyezAV5sofHLc?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

### How to apply conditions

1. **Enable conditional visibility**
  
  - In the element editor, check the **Show / hide conditionally** option.
2. **Set visibility or behavior**
  
  - Choose whether to **show, hide, or disable** the element.
  - Specify whether the action applies when **all, some, or none** of the conditions are met.
3. **Add a condition**
  
  - Click **Add condition** to create a new condition.
4. **Configure the condition**
  
  - Select an input element currently on the page and define how its value should be evaluated.



You can be specific when creating conditions, like:

- Using date and file upload elements as the content that controls the condition.
- Using a regular expression to target text input values.
- Choosing whether conditions based on text input values should be case sensitive or not.



> **NOTE:** Alternatively, select \`Formula is true\` or \`Formula is false\` to create a condition that evaluates a formula allowing access to data from sources outside of the page such as upstream event data or resources.

**

### Looping page elements

Container looping lets you dynamically render page elements multiple times based on a formula input. When enabled, Tines will repeat the container's elements according to the formula's output - whether that's a static number, an array, or dynamic upstream data. 

Containers also support a user driven duplication option which lets users manually add entries to a looped container.



### Enabling looping on a container

To enable looping on a container, navigate to the container on the page you want to add looping for. Under `Advanced` on the right-hand panel there is a **`Loop`** switch. Enabling this switch allows you to enter into a formula input.

There is also an option to enable **User driven duplication**, which allows users to continue adding looped elements to a container.

![](https://www.datocms-assets.com/55802/1753816153-containeroptions.png)

#### Formula input options

Your loop formula in the `Advanced` tab of a container can be:

- A static number (e.g., `3`) - renders elements that many times
- An array (e.g., `ARRAY("hello", "world")`) - renders once per array element
- An array of objects or referenced upstream data (e.g., `event_transform_action.data`)

> **NOTE:** Loop formulas are evaluated once when the page loads. If your loop formula references another page element (e.g., `META.page_elements.my_number_field`), the loop will not update dynamically when that element's value changes. To reflect changes, the page must be reloaded.

#### Naming looping containers

Tines heavily recommends naming your container to something descriptive! This will help you identify in the event output for looping containers which container's values to look for. The default name for containers is simply "Container". More info on this is in the [Accessing the looped data](#accessing-the-looped-data) section.



### User driven duplication

You may also optionally enable **user-driven duplication** of elements when configuring a container. This will render some **add/remove buttons** in the user's rendered-page view. 

At the end of a looping container, after all the Loop Formula elements have been rendered, there will be a `+` Button at the bottom right of the last element. Clicking on this plus button will duplicate and add elements from the original container onto the screen. Similarly, once elements have been manually added, an `x` Button to delete elements will be rendered. Clicking on this button will remove any added elements.

![](https://www.datocms-assets.com/55802/1753888564-add-or-remove-btns.png)

This feature is powerful for allowing users of your page to add their own elements. This works when whether you have a loop formula or not. Also note this will group the elements by container, in the same manner that regular looped elements are grouped. Note that, there will be no loop metadata with these elements, as they are not generated by a formula/upstream data.

### Accessing looping data in elements

You can access elements within looping containers using formula inputs with the `META.page_elements_loop` object.
This can be useful when defining element names, default values, and rich-text content.


When typing `META.page_elements_loop` you have the following two input options:

1. `index` - Supplying `META.page_elements_loop.index` will provide a number from 0 to the number of loop iterations, will output the loop iteration value for the page element at the current index.
2. `current_value` - Typing `META.page_elements_loop.current_value` will output the loop iteration value  for the page element at the current index. If this is an object, rather than a string, you can access nested properties. 
  
  
  
  For example, the array `["hello", "world"]` will display `"hello"` on the first iteration and `"world"` on the second. When looping through more complex structures, like an array of objects, you can access specific object properties (such as `name` or `email`) within each iteration.
  
  If your loop array is the following `JSON` below, then accessing `META.page_elements_loop.current_value.name` will display `**Tines**`.

```json
[
  {
    "name": "Tines",
    "email": "example@tines.io" 
  },
  ...
]
```

Please note that if the user has utilized and added elements via `User driven duplication`, they will not have any of this loop `current_value` nor `index` accessible in formulas / event output. This is because they are generated elements and not derived from any upstream source.

### Working with looped data output

Based on your looping container's name, Tines groups the loop values in the page event's JSON output body as an array. For example, if your looping container is named "Container", it will appear as "container" (lowercased and snaked cased) in the event output payload. 


In the example below, we have a looping container named "Looping intake" - you can see that all values from "looping_intake" in the page (called "Asset management") are organized in an array. Non-looping fields remain appended to the root of the JSON output as usual.

![](https://www.datocms-assets.com/55802/1767874750-screenshot-2026-01-08-at-12-18-53.png)

Each object in the output array includes the corresponding `current_value` from the loop iteration, allowing you to correlate inputs with their source data.

In downstream actions, you can process this array using a looping event transform or an exploding event transform, depending on your needs.

### Collections

Collections are a customizable and shareable index of related pages, images, external links, and rich text. It allows users to discover pages for easy self-servicing.

![](https://www.datocms-assets.com/55802/1765836614-screenshot-2025-12-15-at-22-09-47.png)

Use the tenant dropdown to find all collections in your team.   

**

The team-level view will show you the names, creation dates, update dates, and access control levels for collections in your team. 

## Controlling end-user access

By default, collections can be accessed by users signed into your tenant. Use the button in the top right corner of the collection editor to control who can view your collection.

> **INFO:**
> All collections on cloud tenants created before 16th December 2025 are accessible to anyone with the link and will remain publicly accessible until you update the setting.
> 
> Pages within all collections are only visible to users who have access to them—this has not changed. For example, if a page is restricted to your team, only team members can see and access it from the collection—even if the collection itself is publicly accessible or restricted to your tenant.

**

In total, there are three levels of access control that you can apply to each collection:

- Only team members: only signed-in members of the team that the collection is in can load the collection
- Members of this Tines tenant (default): anyone signed into the Tines tenant can load the collection (even if they’re not a member of the collection's own team).
- Anyone with the link: anyone with knowledge of the collection URL can load the collection.

> **IMPORTANT:**
> Collections do not affect who can view individual pages. For example, a page that is only visible to Team A can be added to a tenant-wide collection in Team A, but a user from Team B cannot see or access it from said collection.
> 
> You can change individual pages' visibilities in their page settings. Learn more in “[Distribution and access control](/docs/pages/distribution-and-access-control/#controlling-end-user-access)”.

## Editing a collection

The collection editor can be found from the story diagram, your team's home, or the collection itself. It is a separate page that allows you to change the emoji, rename a collection, add, reorder, resize, and remove elements in a collection. 

### Collection name and emoji

Your collection should have a unique name. It forms the URL so others can find it. For example, “Ask engineering” will be accessible at `https://tenant.tines.com/collections/ask_engineering`. 

You can change a collection's name or emoji by clicking on them at the top of the collection editor.

![](https://www.datocms-assets.com/55802/1765837135-collection-editor-name-and-emoji.gif)

Be cautious: changing the name may affect bookmarks.

### Adding and removing elements 

Use the collection toolbar in the top left corner to add [various elements](/docs/page-collections#collection-elements) to a collection. 

Use the element toolbar below the selected element to configure the element or delete it. 

**

### Reordering and resizing elements 

Collections allows you to drag and drop to reorder and place your elements anywhere within its grid system.

Use the drag handles on a selected element to resize it. Each element has a number of preset sizes.

**

### Viewing a collection

Preview your collection layout by toggling between the desktop and mobile screen sizes.

Hit "View live" from the top right corner to view the live collection. 

**

### Selecting an element to configure it

There are additional and contextual configuration options for each element type. Select an element to see the available options in the element toolbar.

## Collection elements

You can add a number of element types to a collection from the top left toolbar:

### Pages

Add a page element and use the element toolbar to select which page is displayed in the collection. 

**

Only [pages that kick off a workflow](https://www.tines.com/docs/distribution-and-access-control/) can be added to a collection. These pages have a static URL and can be accessed at any time. 

Pages in a draft or draft stories cannot be added to collections as they are only available as you work on them.

A page can be added to many collections. For example, a page that lets users [submit feedback](https://www.tines.com/library/stories/1190122/capture-product-feedback-and-post-it-to-slack) can be discovered from both internal and external collections.

> **TIP:** Collections do **not** change who can view a page. Find out more in “[Controlling end-user access](#controlling-end-user-access)”.

#### Changing a page emoji

Pages optionally have an emoji that is displayed in collections or case actions. If a page isn't behind change control, you'll be able to change it from the element toolbar.

#### Open the selected page in a new tab

Use the "Open page" button to view the page in a new tab. Useful to remind yourself what a page is for. 

#### Configuring query strings

Power users may want to prefill some page fields with query strings. With this option, the query string will  be propagated to pages loaded inline in a collection.

#### Always open pages in a new tab

By default, pages are loaded inline in a collection. There is an option to always open pages in a new tab in the more menu on the top right of the collection editor.

![](https://www.datocms-assets.com/55802/1765837677-collection-editor-open-page-in-new-tab.gif)

#### Add pages to a collection from the story diagram

You can manage collections by selecting a page from the diagram, then navigating to the Collections section on the right panel. Here, you can add or remove the selected page from your collections. 

You can also view the collection or copy its link for easy sharing from this section.

![](https://www.datocms-assets.com/55802/1702672844-pages-on-the-storyboard.png)

### External links

Add related documentation, tools, or resources living outside of Tines into a collection via the external link element. This lowers context switching for your entire team. 

**

#### Configurations

Every external link element will attempt to fetch the favicon of the target website. It is also possible to change the icon to product logos, Tines icons, emojis, and custom icons via the icon picker. 

Optionally, update the description from the element toolbar to provide more context.

You can view the link in a new tab via the element toolbar to confirm your selection.

### Images

Add pictures and diagrams to your collection via the image element. Upload your image by dragging it over the element, clicking "browse" on the element, or "Replace image" in the element toolbar. JPEG, JPG, PNG, and SVGs are supported at this time.

**

You can change the image sizing via the element toolbar. There are three options: 

- Fill: stretches the image across the entire element
- Contain: contains the image within the element while maintaining its aspect ratio
- Cover (default): size and clip the image to ensure it spans the entire element while maintaining its aspect ratio

These options corresponds to the [`object-fit` CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/object-fit). 

### Rich text

The rich text element is a WYSIWYG editor that allows you to preview markdown as you write it. Use this element to give your visitors more info about the collection. 

**

The `/` command brings up all possible markdown block types for easy addition, including simple paragraphs, lists, code blocks, tables, callouts, and more.

Highlighting any text brings up a toolbar to change your block type, font type, or font color. 

Make your rich text element more prominent by changing the background color via the element toolbar.

This element type grows as you type but allows you to resize it via the drag handlers like all other elements.

### Custom domain for pages

> **TIP:** To enable custom domains on your pages (and webhooks), reach out to your CSM, CSE or Tines Support, and they will go through the process of setting one up with you.

With custom domain for pages enabled, all pages (including those in existing stories built prior to configuring a custom domain) will be switched to the custom domain. However, all pages will still be accessible via the original URL. For any pages that are protected by SSO authentication that you want available on the custom domain, you will need to set up secondary SSO for this domain in `Authentication Settings.`

## Resources

## Introduction

It's common for teams to use the same piece of information in multiple actions. For example, a list of known-good domains. If this information were stored directly inside the actions' options and changed (a new known-good domain is added), every action would need to be updated individually. **Resources** provide a way to store information in a single, centralized location, so it can be accessed from any action in any story in your team or teams it has been shared with.

Resources are similar to [global variables](https://en.wikipedia.org/wiki/Global_variable) in software development.

## Creating a resource

Within your team, select your team name on the top left and then select "Resources". 

On the Resources page, you will see a list of existing Resources in your team. To create a new one, select `+ New` in the top right corner. Resources can be plaintext, a JSON object such as an array, or a file. There is a 5MB size limit on Resources. Enter your desired information and select `Save resource`.

An alternate way to create a Resource is from the storyboard. On the right hand panel, identify the Resources section. To create a new Resource, select the `+` next to the word Resources. 

## Using a Resource in an action

To use a Resource, reference it [with the `RESOURCE` key in formulas](https://www.tines.com/docs/formulas/referencing-data#from-resources) in your desired action. When the action runs, the Resource placeholder will be replaced by the contents stored in the referenced Resource.

## Test details tab

If you are using [change control](https://www.tines.com/docs/stories/change-control/) in a story, you may want to test changes with your developer environment before deploying them to the live story. You can define Test Resource data in the `Test Details` tab of the Resource. In stories with change control enabled, the Test Resource will be used in any `draft` of the story by default. See our [change control](https://www.tines.com/docs/stories/change-control/#use-different-resources-and-credentials-in-a-draft) documentation for further details.

## Sharing a Resource

Resources are, by default, only accessible to the [Team](https://hub.tines.com/docs/admin/teams) they are created within. Resources can be configured to be shared with all other teams in the tenant by selecting the 'All teams' access option; this will include personal teams. Additionally, Resources can be shared with other teams specifically by selecting the desired team name.

> **NOTE:** Need to share resources with other teams? [Reach out to your Tine contact or support](https://www.tines.com/contact-support) to discuss adding this feature to your Tines tenant.

### Using a shared resource

Resources with the same name as Resources shared across multiple teams will use the Resource located within the same team as the story.

You can view all Resources shared with your team by clicking the "Shared with this team" section in the Resources page. You will not be able to modify the contents of the Resource unless you have the relevant permissions in the team that owns the Resource.

## Resource locking

Resources can be locked, meaning that they cannot be edited. This ensures that the contents of a Resource will remain static, which can be essential for Resources that are used by multiple high priority stories. Only team admins and [users with permission to manage Resources](https://www.tines.com/docs/admin/user-administration/custom-roles#feature-permission-list) are allowed to lock or unlock resources.

A resource can be locked by clicking the **Lock resource** button on the top-right of the resource modal.

![](https://www.datocms-assets.com/55802/1721387834-screenshot-2024-07-19-at-11-36-01.png)

While locked, it won't be possible to edit any of the Resource's contents.

Afterwards, clicking the **Unlock to edit** button will *temporarily* unlock the resource. This will allow this user to edit the Resource once. The Resource will not be unlocked for anyone except the user who clicked the unlock button, and if they exit without editing the Resource it will be locked again.

![](https://www.datocms-assets.com/55802/1721387897-screenshot-2024-07-19-at-12-18-14.png)

If a user wants to permanently unlock the Resource for all users, they can do so by selecting **Permanently unlock** from the dropdown.

![](https://www.datocms-assets.com/55802/1721387995-screenshot-2024-07-19-at-12-18-32.png)

## Cases

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

Cases are a collaborative interface where you can organize information, comment, take action, and report on progress all in real-time. 

![](https://www.datocms-assets.com/55802/1769459535-cases-example2.png)

Here are just a few ways our customers use cases:

- [Triage suspicious alerts](https://www.tines.com/library/stories/1318807) or SIEM events using playbook or SOP
- [Evaluate IOCs](https://www.tines.com/library/stories/1235821) and [manage observables](https://www.tines.com/library/stories/1257293/?name=case-lifecycle-management) with detailed evidence
- [Investigate AWS ](https://www.tines.com/library/stories/1321356)issues via CLI
- [Deallocate and delete](https://www.tines.com/library/stories/1191903/?name=deallocate-and-delete-unnecessary-virtual-machines-in-azure-and-raise-tickets) stale virtual machines in Azure

Whether you're running down a security incident, managing a support request, triaging a flood of alerts, or coordinating an operational task, cases can flex to your needs.

> **TIP:** Check out the Tines University learning path for an in-depth course on becoming a [Cases specialist](https://www.tines.com/university/specialist-cases/).

---

## Getting started with cases

Cases can be opened manually with a few clicks inside Tines, or automatically by a story automation.

We recommend starting with a case template and building out content from there. 

Use  [case notifications](https://www.tines.com/docs/case-notifications/) and the [action template library](https://www.tines.com/docs/templates/) to interact with your case content.

### Navigating a case

Cases put information at your fingertips while staying flexible enough to fit your workflow. This [article](https://www.tines.com/docs/layout/) breaks down the layout of a case and how to navigate it's structure.

### Components of a case

Within the case structure are various components to display and interact with case data. This [article](https://www.tines.com/docs/components/) lists major case components and provides a summary of how they work.

### Working with records and dashboards

Records and dashboards are a powerful addition to building your cases workflow.

![](https://www.datocms-assets.com/55802/1767126003-cases-records-dashboards.png)

### Records

When you need to capture, transform, or reference structured data within your story, use a [record](https://www.tines.com/docs/records/). When **linked** to a specific case a record can provide helpful context. 

### Dashboards

You can then visualize that data from records and cases in a single view with [dashboards.](https://www.tines.com/docs/records-cases/dashboards) These views help you monitor what's happening across workflows within your team. 

## Trying out cases

Cases are an add-on feature for paid plans. By purchasing cases, you also gain access to records and dashboards. 

[Click here](https://hq.tines.io/pages/c88e1a237bed1f2e43fcf39d597ca6cd/) to start a one-time, 45-day trial.

---

## Learning more about cases

Continue reading to learn more about cases:

1. [Creating a case](https://www.tines.com/docs/creating-a-case/)
2. [Using case notifications](https://www.tines.com/docs/case-notifications/)
3. [Case groups](https://www.tines.com/docs/case-groups/)

Also be sure to explore some examples of case automations [in our library](https://www.tines.com/library/cases-records), and take a look at our latest [case product updates](https://www.tines.com/whats-new?category=cases).

### Overview of cases

## Viewing cases 

The cases home provides a complete view of all open cases by default. Users triaging cases will start in the cases home within their assigned team. See default and saved case views within your team navigation bar under `Cases`. Select the default case view `assigned to me` to quickly filter for cases assigned to your user, or create a custom saved view, which will appear below `assigned to me` for efficient case review. To create a custom saved view, select the bookmark icon next to your applied case filters; case views are visible by all members of a team with access to cases.

**

### Filter cases

You can filter cases by any of the below items. Multiple filters can be applied at once.

- Search: search against case name, description and ID.
- Tags: select the tags associated with the cases you’d like to see
- Assignee: filter for cases assigned to a person or cases that are currently unassigned
- Status: view only open or closed cases
- Priority: view only cases set with a specific priority
- Comment: search against comment contents
- Metadata: filter for a specific key-value metadata pair
- Record: filter for cases with a specific record value
- Author: filter for cases created by a specific user
- Date: filter for cases created or resolved during a specified date range
- SLA status: filter for cases with a exceeded or warning SLA status
- SLA type: filter by completion or response SLA types
- Note: search against note contents



### Sort cases

You can sort cases by any of the below attributes:

- Activity (newest first)
- Activity (oldest first)
- Opened (newest first)
- Opened (oldest first)
- Created (newest first)
- Created (oldest first)
- Priority (low to high)
- Priority (high to low)



## Bulk actions on cases 

From the cases home, users can apply the below actions to bulk selected cases:

- Update status
- Set priority
- Add or remove assignees
- Add or remove tags
- Add a comment

**

### Download cases as csv

While you can export individual cases as pdfs, you can export filtered lists of cases as csvs. After applying your desired filters, select the kebab menu next to the number of cases and select `Download`. 

![](https://www.datocms-assets.com/55802/1745595498-exportcases.png)

The export details will be downloaded with the following case attributes: case ID, name, status, sub_status, severity, tags, team, opened at, resolved at, opened by, resolved_by, metadata, fields, and assignees.

### Export cases to email

You can export filtered lists of cases as a csv to your email. After applying your desired filters, select the kebab menu next to the number of cases and click `Email`.  

![](https://www.datocms-assets.com/55802/1745596469-exporttoemail.png)

The export details will be emailed to you with the following case attributes: case ID, name, status, sub_status, severity, tags, team, opened at, resolved at, opened by, resolved_by, metadata, fields, and assignees.

## Case webhook notifications 

Whenever a case is created or updated, a **case notification** is generated. You can subscribe to these notifications by linking a [webhook action](https://www.tines.com/docs/webhook/) to your team's case settings.

To get started, create a webhook action and paste its URL in the `Cases` > `Case settings` > `Notifications` config.

See the [case notifications](https://www.tines.com/docs/case-notifications/) article for more information.

**

## Keyboard shortcuts

Press `Shift+K` to view the full list of keyboard shortcuts for cases.

![](https://www.datocms-assets.com/55802/1719178979-keyboard-shortcut-image.png)

#### Navigate a case

Understand the key components of a case and how they work together.

## Case structure

The case layout uses a visual hierarchy. When you open a case, start at the top left to check the case status and key details, then work your way down and right to review content and access tools.

The topics of this article correspond to each section in the diagram below.

1. Navigation path
2. Controls
3. Title, properties
4. Table of contents
5. Content
6. Sidebar

![](https://www.datocms-assets.com/55802/1769147453-cases-layout.png)

---

## Navigation path

When you open a case, you'll see where it lives in your team structure.

Cases live within a team or in a Team's case groups. The **navigation path** shows breadcrumbs to help you understand where the case is in the hierarchy.

```
Team > Case group > "Cases" > Case name & ID
```

The navigation path also displays a hint with a few key details about the case, specifically:

- Case ID
- How long ago the case was created
- Who created the case
- Number of user comments

![](https://www.datocms-assets.com/55802/1769465625-cases-navigation-v2.png)

---

## Controls 

Next are controls for managing the case:

- Number of case tasks remaining vs completed
- Navigational arrows to advance to the previous or next case assigned to the current user
- Notifications and configuration for subscriptions
- Case status selection
- Options & controls
  
  - Copy the direct link to the case
  - Subscribe/unsubscribe to the case
  - Export the case as a PDF
  - Delete the case
  - Hide/show the case sidebar
  - Toggle wrapping of code blocks
  - Collapse/expand all blocks

![](https://www.datocms-assets.com/55802/1769465680-cases-controls-v2.png)

---

## Title and properties

This section shows case details and metadata:

- Severity of the case (Critical, High, Medium, Low, Info)
- Subscribers to be notified of case changes (users are subscribed by opting-in via the control options or from being mentioned in a comment) as well as current viewers of the case
- Assignee(s)
- Case title
- Tags
-  Info icon—click to view timing and activity metadata
  
  - Timing metadata: `Created at`, `updated at`, `resolved at`, `time lapsed since creation`
  - User metadata: `Created by`, `last comment from`

![](https://www.datocms-assets.com/55802/1769465696-cases-title-v2.png)

---

## Table of contents

Navigate case contents using the table of contents. It showcases the case description and structure of case blocks.

##### Click to navigate to a section

Each entry is a header in the case description, a case block group, or a case block. Click any section to quickly scroll and display the description heading or case block.

##### Spotlight one or more sections

When hovering on a section in the table of contents, the spotlight icon will appear. Click this icon to hide all content except the target description header, case group, or case block. 

Easily compare content between specific case sections by clicking more than one section's spotlight icon. 

##### Reorder sections by dragging

Click and drag a case block to re-order it within the case structure. You can also drag the case block into and out of case groups. Headings and the description section cannot be moved - they are pinned to the top of the case table of contents.

##### Show and hide sections

Select `Hide` from the options menu within each case block to hide it from displaying in the case content. Clicking a hidden case block will display it's content similar to spotlighting unhidden sections.

##### Show and hide the table of contents sidebar

The table of contents is located in the left sidebar. This sidebar is shown by default but can be configured to a "peek" mode by clicking the `Contents` button at the top of the sidebar. This will temporarily hide the table of contents sidebar until you position your mouse near the left edge of a case. It will then peek out for interaction before hiding again.

![](https://www.datocms-assets.com/55802/1769465747-cases-toc-v2.png)

---

## Description and content

Build and view the core content of your case here.

Case content uses [CommonMark](https://commonmark.org/) formatting. When you type and format content, it's stored as markup. When you interact with APIs and stories, you can view and edit the raw markup.

The content area is broken up into two sections: **Description** and **Case blocks**. Every case has a description block. Case blocks are optional.

![](https://www.datocms-assets.com/55802/1769465770-cases-content-v2.png)

##### Description section

The description is the beginning of the case content and is followed by case blocks. It supports a large volume of text and the contents of the description are included in search results. You cannot re-order the description within the case structure or table of contents.

##### Case blocks and groups

Click each button in the toolbar at the bottom of the case to add content below the description section. Cases use a concept called "Blocks" to help structure and format your content. Each block can be added via the **case toolbar** at the bottom of the case below. Add blocks to the case, then add your content.

- Blocks can be grouped together
- You can re-arrange blocks or groups of blocks using the table of contents or options menu within a block
- Blocks can be set to one of 8 pre-defined colors.

##### References & formatting

- Use the `/` shortcut to open the formatting menu. The formatting options support Headings, bullet lists, ordered lists, task lists, a code block, a quote, inserting an image via link, pasting an image, a table, or a callout.
- Use the `@` shortcut to open the reference menu. The reference options support mentioning stories, users, pages, other cases, as well as blocks within the current case. You can also include shortcuts to case primitives like fields and case actions.

##### Record section

Create, attach, view, filter, search and remove [records](https://www.tines.com/docs/records/) that have been linked to the case. This section is pinned to the bottom of the case.

##### Case toolbar

<table border="1" height="266" style="border-collapse: collapse;"><tbody><tr><td><p><strong>Toolbar button</strong></p></td><td><p><strong>Icon</strong></p></td><td><p><strong>Details</strong></p></td></tr><tr><td><p><strong>Note block</strong></p></td><td><p><img src="https://www.datocms-assets.com/55802/1769636924-block-note.png"></p></td><td><p>Add helpful notes to your case that can be titled, colored, and formatted.</p><p>The content of a note block is included in search results.</p></td></tr><tr><td><p><strong>HTML block</strong></p></td><td><p><img src="https://www.datocms-assets.com/55802/1769636924-block-html.png"></p></td><td><p>A block that supports rendering basic HTML syntax.</p><p>The content of an HTML block is not included in search results.</p></td></tr><tr><td><p><strong>Attachment block</strong></p></td><td><p><img src="https://www.datocms-assets.com/55802/1769636924-block-file.png"></p></td><td><p>A block to add one or more attachments. Each attachment can be annotated with a direct comment.</p></td></tr><tr><td><p><strong>Table block</strong></p></td><td><p><img src="https://www.datocms-assets.com/55802/1769636924-block-note-with-table.png"></p></td><td><p>A note block that's pre-formatted with a table.</p></td></tr><tr><td><p><strong>Code block</strong></p></td><td><p><img src="https://www.datocms-assets.com/55802/1769636924-block-note-with-code-snippet.png"></p></td><td><p>A note block that's pre-formatted with a code snippet.</p><p>This text can be formatted as text, C, C++, CSS, HTML, Java, JavaScript, JSON, Markdown, Python, SQL, and YAML.</p></td></tr><tr><td><p><strong>Group of blocks</strong></p></td><td><p><img src="https://www.datocms-assets.com/55802/1769636924-block-group.png"></p></td><td><p>Add a case group, then drag case blocks into the case group using the table of contents or the options menu on a case block.</p></td></tr><tr><td><p><strong>Load in workbench</strong></p></td><td><p><img src="https://www.datocms-assets.com/55802/1769636924-block-workbench.png"></p></td><td><p>Load the current case into a workbench chat for AI-assisted case analysis and interaction.</p></td></tr></tbody></table>

---

## Case sidebar

The sidebar keeps essential tools and context within reach while you work. Customize it to fit your workflow by reordering sections and chosing which case tools are displayed.

![](https://www.datocms-assets.com/55802/1769465788-cases-sidebar-v2.png)

### **Interacting with the sidebar**

Click each section header of the sidebar to show or hide the section's details. When a section is collapsed, you can then drag the section up and down to prioritize what's important to you.

**

<table border="1" height="829" style="border-collapse: collapse;"><tbody><tr><td><p><strong>Sidebar sections</strong></p></td><td><p><strong>Description</strong></p></td></tr><tr><td><p><strong>Activity &amp; comments</strong></p></td><td><p>All case activity and comments are listed here. Filter and search for relevant activity, or leave a comment about the case. Comments can be formatted using the <code>/</code> shortcut. When mentioning another user in a comment with the <code>@</code> shortcut, they will automatically be subscribed to the case.</p></td></tr><tr><td><p><strong>Case actions</strong></p></td><td><p>Display actions (workflow and page shortcuts) to quickly enrich, augment, or interact with case content. Add or update case actions here. Actions can be referenced in the case description, case notes, or comments by typing the <code>@</code> shortcut.</p></td></tr><tr><td><p><strong>Case fields</strong></p></td><td><p>Display fields (structured key-value pairs) that have been added to the case. Add or update fields directly. Fields can be inserted into the case description, case notes, or comments by typing the <code>@</code> shortcut and the field name. Once a field is referenced in the case content, it can be interacted with and updated.</p></td></tr><tr><td><p><strong>Linked cases</strong></p></td><td><p>Display case links to other cases. Add or remove linked cases here. Hovering on a case shows a summary of the target linked case.</p></td></tr><tr><td><p><strong>Tasks</strong></p></td><td><p>Display <strong>tasks</strong> and <strong>closure requirements</strong> for the case. Tasks can be assigned, updated, or completed. Closure requirements cannot be added here. Instead, they must be added in the case template, at the time of case creation, or can be updated on an existing case via <a href="https://www.tines.com/api/cases/update/" target="_blank" rel="noopener">API</a>.</p></td></tr></tbody></table>

> **TIP:**
> You can expand and collapse sections quickly using the following keyboard shortcuts:
> 
> -   Activity: `Shift + H`
> -   Fields: `Shift + F`
> -   Actions: `Shift + A`
> -   Tasks: `Shift + T`
> -   Linked cases: `Shift + L`
> 
> See the [shortcuts page](#datocms-record-Np6hqJeGSLeciIFesEsPig) for more information.

### **Configuring the sidebar**

Sidebar customization is saved locally in your browser. Your section order and expand/collapse preferences persist across all cases, case groups, and teams.

The sidebar's configuration cannot be centrally managed. 

> **NOTE:** Sidebar customization is browser-specific. Using a different browser or incognito mode will reset the sidebar to its default collapsed state.

##### Resizing the sidebar

Adjust the sidebar width by dragging the divider between the case content and sidebar. The sidebar can expand up to half your window width.

##### Hide and show the sidebar

Use the case options menu in the top-right to hide or show the sidebar. Alternatively, use  `⌘ + ]`  (Mac) or `ctrl + ]` (Windows) as a [keyboard shortcut](https://www.tines.com/docs/shortcuts/).

#### Components of a case

## Case roles

All users assigned to a default role in a team will be able to view cases. To restrict this further, explore custom roles.

##### Case manager role

This role is designed for users who work with cases and do not need access to many other aspects of the system. This role gives the user read-write permissions to [cases](https://www.tines.com/docs/records-cases/cases), while restricting access to other objects on the team (stories, resources, credentials, events, etc.).

##### Custom roles

To define custom permissions for users such as a case viewer, create a custom role.

> **INFO:**
> To learn more about default roles including the Case Manager role, see [Team roles](https://www.tines.com/docs/admin/teams#team-roles).
> 
> To learn more about designing custom roles, see [Custom roles](https://www.tines.com/docs/admin/user-administration/custom-roles).



### Closure requirements

If you need to ensure certain actions or steps are taken before a case can be closed you can add a closure requirement to the case. 

Closure requirements are set at case creation time, they take the form of a Formula that must evaluate to true for the given case to close. They can be set via the API or the Case creation tile.

![](https://www.datocms-assets.com/55802/1712759050-screenshot-2024-04-10-at-3-24-00-pm.png)

We support referencing the attributes of the case itself using the Tines Formula language, so you can access the case description using `team_case.description`

![](https://www.datocms-assets.com/55802/1712759184-screenshot-2024-04-10-at-3-25-58-pm.png)

Case admins can choose to override a closure requirement but it is logged that the admin overrode the requirement. 

## Subscribe to cases

Specify case subscription settings via the case settings notifications menu. 

![](https://www.datocms-assets.com/55802/1708107464-case-notif-settings.png)

Users can subscribe and unsubscribe to individual cases via the kebab menu at the top right of a case screen. If you comment on a case, you will be automatically subscribed. View all case subscribers by selecting the orange eye icon next to assignees.

![](https://www.datocms-assets.com/55802/1708107532-subscribe-to-a-case.png)



##### Notifications within Tines

Subscribers will be notified of changes within Tines via the notifications menu. Specify your preferred notification settings via notification settings.

**

##### Notifications outside of Tines

Case subscribers are recorded within case information via API. If you would like to notify subscribers outside of Tines of changes, via Slack for example, you can build a custom notification using your case webhook. See [here](https://www.tines.com/library/stories/1197267/send-tines-case-notifications-to-subscribers-in-slack?redirected-from=%2Flibrary%2Fcases-records%3Fview%3Dall&s=subs) for an example.



## Exporting case data

##### Export as a pdf

Export individual case details as a pdf via the kebab menu on the top right of a case. The export will be emailed to you as a pdf with all relevant case details included.

![](https://www.datocms-assets.com/55802/1708107730-export-as-pdf.png)

##### Export as a csv

Export filtered lists of cases as csvs from the Cases home page. After applying your desired filters, select the kebab menu next to the number of cases and select `Export as CSV`. The export details will be emailed to you with the following case attributes: case ID, name, status, severity, tags, team, opened at, resolved at, opened by, metadata, and assignees.

![](https://www.datocms-assets.com/55802/1708103695-export-as-csv.png)

## Case statuses

A case's status may be updated either from the case list, case view, or API.

By default, Tines provides four default statuses: `To do`, `In progress`, `Done`, and `Cancelled`

You may also create your own custom statuses via the `Manage statuses` option in the status dropdown:

**

## Case SLAs

Cases can be assigned time to resolve SLAs per priority level. These can be found by clicking the settings button on the case list page.

**

Timeline actions will be created when a case has passed 75% of its deadline and also when exceeding it.

![](https://www.datocms-assets.com/55802/1716222028-screenshot-2024-05-20-at-17-20-02.png)

Reporting is possible by filtering cases by `Warning` or `Exceeded`.

**

## Case groups 


[Case groups ](https://www.tines.com/docs/case-groups/)provide a way to segment cases, case views, and dashboards within a team. Teams can use them to create specialized groups that respond to different types of cases, such as those in an IT help desk or a security incident response team.

**

## Case fields

With case fields, you can define custom variables at the team level, each with a specified type— text, number, timestamp, or boolean. You can define up to 100 case fields per team.

You can define a new case field and validations from the case settings modal.

**

Once defined, these variables can be assigned values within individual cases. You can easily reference these values in case descriptions or comments by typing "@" followed by the name of the desired case field.

**

To edit a field’s value for a specific case, use the new “Fields” menu located in the right-hand side panel, or simply click on the reference within the case description.

**

We also offer an API for case fields. You can find the documentation [here.](https://www.tines.com/api/cases/fields/create/)

## Case actions

Case actions let you add interactive automation hooks into your case. These hooks appear in the form of buttons added to the case.

![](https://www.datocms-assets.com/55802/1712758098-screenshot-2024-04-10-at-3-07-55-pm.png)

We support two types of actions:

- **Webhook**: This action type will trigger a call to the designated webhook, passing the case details to the given URL in the background and is useful for async processes
- **Page**: This is used for interactions where the user may need to fill in details, it will open the designated page in a new tab

For all types, you can edit the label and button text to make it clear the exact call to action expected of users.

#### Case roles and permissions

Control who can view, edit, and manage specific parts of a case using granular role-based access control (RBAC).

## When to use granular permissions

By default, Cases uses broad permissions — "Editors" and "Case managers" can perform most actions, while "Viewers" have read-only access. Granular RBAC lets you go further by controlling access to individual case components like comments, attachments, fields, and status transitions. 

> **TIP:** Custom roles is not available on every license plan. Please reach out to the account team to incquire about this capability.

Use granular permissions when you need to:

- Restrict sensitive case data to specific team members
- Create specialized roles (e.g., a triage analyst who can update status but not delete cases)
- Enforce least-privilege access across large teams
- Protect fields marked as sensitive from broad visibility

## How it works

Cases permissions operate at two levels:

1. **Broad permissions:** "Manage cases" and "Read cases" control overall case access. Manage cases bypasses all granular checks.
2. **Granular permissions: **Individual read and write permissions for each case component (comments, files, fields, status, etc.).

> **WARNING:** **Broad permissions** will soon be deprecated in favor of **granular permissions**. We recommend planning your roles accordingly.

Permissions are *non-cumulative*. A higher-level action does not imply lower ones. For example, "Write comments" does not automatically grant "View comments". You must assign both explicitly.

## Default team roles

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 50.237%;"><col style="width: 49.763%;"></colgroup><tbody><tr><th>Role</th><th>Cases access</th></tr><tr><td><strong>Team admin</strong></td><td>All permissions</td></tr><tr><td><strong>Editor</strong></td><td>Full case read + nearly all write permissions</td></tr><tr><td><strong>Case manager</strong></td><td>Read cases + nearly all write permissions (no story or credential access)</td></tr><tr><td><strong>Viewer</strong></td><td>Read-only + View tasks</td></tr><tr><td><strong>Workbench user</strong></td><td>None</td></tr><tr><td><strong>Dashboard manager</strong></td><td>None (dashboard access only)</td></tr><tr><td><strong>Dashboard viewer</strong></td><td>None (dashboard access only)</td></tr></tbody></table>

Editors and case managers share the same Cases write permissions. The difference is that editors also have access to stories and credentials, while case managers are scoped exclusively to Cases.



## Permissions reference

### Core case operations

#### Core case management

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.6206%;"><col style="width: 25.5102%;"><col style="width: 14.881%;"><col style="width: 19.6051%;"><col style="width: 16.2982%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Manage cases</strong></td><td>Bypasses all granular RBAC checks. Grants record update access including parent team.</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Read cases</strong></td><td>List and view cases</td><td>✓</td><td>✓</td><td>✓</td></tr><tr><td><strong>Create cases</strong></td><td>Create new cases</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update cases</strong></td><td>Update case name and basic fields</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete cases</strong></td><td>Permanently delete cases</td><td>–</td><td>–</td><td>–</td></tr></tbody></table>

#### Bulk manage cases

<table border="1" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background-color: #ffffff; font-size: 15px; font-weight: 400;"><colgroup><col style="width: 23.6206%;"><col style="width: 25.5102%;"><col style="width: 14.881%;"><col style="width: 19.6051%;"><col style="width: 16.2982%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Bulk manage cases</strong></td><td>Use the bulk update tool - requires componenet permissions to specify what can be updated in bulk</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

#### Security controls

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.6206%;"><col style="width: 25.5102%;"><col style="width: 14.881%;"><col style="width: 19.6051%;"><col style="width: 16.2982%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Override closure conditions</strong></td><td>Override closure conditions when updating status</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>View sensitive information</strong></td><td>View case fields marked as sensitive</td><td>–</td><td>–</td><td>–</td></tr><tr><td><strong>Update case security settings</strong></td><td>Update team-level case security settings</td><td>–</td><td>–</td><td>–</td></tr></tbody></table>

### Case details

**Assignee**

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Update case assignees</strong></td><td>Add or remove assignees</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

**Subscribers**

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Subscribe to cases</strong></td><td>Subscribe users to cases</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Unsubscribe from cases</strong></td><td>Unsubscribe users from cases</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

**Priorities**

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Update case priority</strong></td><td>Change case priority</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

**Statuses**

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Create case statuses</strong></td><td>Create new status definitions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update case statuses</strong></td><td>Update existing status definitions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete case statuses</strong></td><td>Delete status definitions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update case status</strong></td><td>Change the status value on a case</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

#### Tags

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Update case tags</strong></td><td>Add or remove tags</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

### Case content

#### Description

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 12.404%;"><col style="width: 25.0421%;"><col style="width: 14.7444%;"><col style="width: 19.4251%;"><col style="width: 16.1486%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Update case description</strong></td><td>Update case descriptions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update case task status</strong></td><td>Update task status checkboxes within descriptions</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

**Case Content**

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 12.404%;"><col style="width: 25.0421%;"><col style="width: 14.7444%;"><col style="width: 19.4251%;"><col style="width: 16.1486%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Create block</strong></td><td>Create a case block</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update block</strong></td><td>Update a case block</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete block</strong></td><td>Delete a case block</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

**Attached files**

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 12.404%;"><col style="width: 25.0421%;"><col style="width: 14.7444%;"><col style="width: 19.4251%;"><col style="width: 16.1486%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Attach files</strong></td><td>Attach files to cases</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete attachments</strong></td><td>Delete file attachments</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>



### Case interaction sidebar

Interacting with components in the cases sidebar

#### Comments & reactions

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col><col></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Write comments</strong></td><td>Create comments</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update comments</strong></td><td>Update comments</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete comments</strong></td><td>Delete comments</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Add comment reactions</strong></td><td>Add reactions to comments</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete comment reactions</strong></td><td>Delete reactions</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

**Case actions**

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col><col></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Create actions</strong></td><td>Create actions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update actions</strong></td><td>Update actions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete actions</strong></td><td>Delete actions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Run actions</strong></td><td>Run/trigger actions on cases</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

#### Case fields

<table border="1" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background-color: #ffffff; font-size: 15px; font-weight: 400;"><colgroup><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col><col></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Add fields to cases</strong></td><td>Add an existing field to a case</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update field values</strong></td><td>Update or reset field values</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Remove fields from cases</strong></td><td>Remove a field from a case</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

*See "View sensitive information" permission for field sensitivity under "Core case operations".*

#### Links

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col><col></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Link cases</strong></td><td>Link cases to each other</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Unlink cases</strong></td><td>Unlink cases from each other</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

#### Tasks

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col style="width: 33.3333%;"><col><col></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>View tasks</strong></td><td>View tasks in cases</td><td>✓</td><td>✓</td><td>✓</td></tr><tr><td><strong>Create tasks</strong></td><td>Create tasks</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update tasks</strong></td><td>Update tasks</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete tasks</strong></td><td>Delete tasks</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Assign tasks</strong></td><td>Assign tasks to users</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Complete tasks</strong></td><td>Mark tasks as complete or incomplete</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

### Case search and filtering

Using the case search and filter view:

#### Saved case search views

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Create saved views</strong></td><td>Create saved views</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update saved views</strong></td><td>Update saved views</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete saved views</strong></td><td>Delete saved views</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

### Case Templates

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 25.2747%;"><col style="width: 25.2747%;"><col style="width: 25.2747%;"><col style="width: 25.2747%;"><col></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Create case templates</strong></td><td>Create new templates</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update case templates</strong></td><td>Update existing templates</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete case templates</strong></td><td>Delete templates</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

### Metadata & Records

#### Metadata

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td>Update metadata</td><td>Update case metadata</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

#### Records attached to a case

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td>View case records</td><td>View records associated with a case</td><td>–</td><td>–</td><td>–</td></tr><tr><td>Add records to cases</td><td>Add a record to a case</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td>Delete case records</td><td>Delete records from a case</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

### Team case settings

Updating the settings within each team specific to cases:

#### Field definitions

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 24.2706%;"><col style="width: 24.2706%;"><col style="width: 15.1391%;"><col style="width: 19.9452%;"><col style="width: 16.5809%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Create field definitions</strong></td><td>Create new field definitions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update field definitions</strong></td><td>Update existing field definitions</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete field definitions</strong></td><td>Delete field definitions</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

#### SLA durations

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 22.6328%;"><col style="width: 27.9446%;"><col style="width: 14.5497%;"><col style="width: 19.1686%;"><col style="width: 15.9353%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Create SLAs</strong></td><td>Create SLA rules</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Update SLAs</strong></td><td>Update SLA rules</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Delete SLAs</strong></td><td>Delete SLA rules</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>

#### Case notification webhooks

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 23.7216%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td>Create case webhooks</td><td>Create webhooks</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td>Update case webhooks</td><td>Update webhooks</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td>Delete case webhooks</td><td>Delete webhooks</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>



## Permissions not granted to any default role

The following permissions are only available to team admins or through [custom roles](https://www.tines.com/docs/custom-roles/):

- **Delete cases** — permanently delete cases
- **View sensitive information** — view fields marked as sensitive
- **Update case security settings** — update team-level case security settings

## Best practices

- **Start with default roles, then customize.** Default roles cover most use cases. Only create custom roles when you need to restrict or extend specific permissions.
- **Reserve Delete cases for admin-level custom roles.** Deletion is irreversible — keep it tightly controlled.
- **Audit custom roles periodically.** As your team grows, review whether custom role assignments still reflect actual responsibilities.
- **Remember permissions are non-cumulative.** Always pair write permissions with their corresponding read permissions, or users may be able to create data they cannot view.

## Learn more about custom roles

See [custom roles](https://www.tines.com/docs/custom-roles/) for additional details on defining custom roles.

#### Case shortcuts

There are a number of shortcut utilities built into Tines to reduce the time spent navigating cases. 

> **TIP:** Where you see the `⌘` key for Mac shortcuts, use the `ctrl` key for Windows.

## Shorcuts menu

The keyboard shortcuts menu provides quick access to commands based on your current context. It displays different options depending on where you are in the app. See this [shortcuts page](https://www.tines.com/docs/shortcuts/) for additional shortcuts across Tines.

> **NOTE:** If you hit `⇧(Shift) + K` anywhere in Tines, it will bring up the keyboard shortcuts menu.

## Mentions & formatting shortcuts

When interacting with case text inputs (case notes, description, or comments), use the following shortcuts to choose formatting or mentions options.

<table xmlns="http://www.w3.org/1999/xhtml" cellspacing="0" cellpadding="0" dir="ltr" border="1" data-sheets-root="1" data-sheets-baot="1"><colgroup><col width="100" style="width: 48.2353%;"><col width="100" style="width: 51.4706%;"></colgroup><tbody><tr><td>Keyboard shortcuts</td><td><div><div>Description</div></div></td></tr><tr><td><code>@</code></td><td>Mention a user, case field, note block, another case, a case action, a specific page, or a specific story.</td></tr><tr><td><code>/</code></td><td><div><div>Open the formatting options menu.</div></div></td></tr></tbody></table>

## Interaction shortcuts

<table xmlns="http://www.w3.org/1999/xhtml" cellspacing="0" cellpadding="0" dir="ltr" border="1" data-sheets-root="1" data-sheets-baot="1" height="1140"><colgroup><col width="100" style="width: 48.2353%;"><col width="100" style="width: 51.4706%;"></colgroup><tbody><tr><td>Keyboard shortcuts</td><td><div><div>Description</div></div></td></tr><tr><td><code>S</code></td><td><div><div>Open status menu</div></div></td></tr><tr><td><code>↩</code></td><td>Open case</td></tr><tr><td><code>C</code></td><td><div><div>Mark case done/undone</div></div></td></tr><tr><td><code>O</code></td><td><div><div>Open case in new tab</div></div></td></tr><tr><td><code>Space</code></td><td>Select case</td></tr><tr><td><code>A</code></td><td><div><div>Assign case to self</div></div></td></tr><tr><td><code>⇧ S</code></td><td><div><div>Toggle case subscription</div></div></td></tr><tr><td><code>D</code></td><td>Delete case</td></tr><tr><td><code>⌘ J</code></td><td><div><div>Toggle right panel</div></div></td></tr><tr><td><code>⌘ C</code></td><td>Copy actions</td></tr><tr><td><code>⌘ ⇧ →</code></td><td><div><div>Next open assigned case</div></div></td></tr><tr><td><code>⌘ ⇧ ←</code></td><td><div><div>Previous open assigned case</div></div></td></tr><tr><td><code>P</code></td><td><div><div>Open priority menu</div></div></td></tr><tr><td><code>C</code></td><td>Focus comment</td></tr><tr><td><code>⌘ ]</code></td><td>Toggles the cases (right) sidebar</td></tr><tr><td><code>⇧ H</code></td><td>Expand/Collapse <strong>activity</strong> sidebar section</td></tr><tr><td><code>⇧ F</code></td><td>Expand/collapse<strong> case fields</strong> sidebar section</td></tr><tr><td><code>⇧ A</code></td><td>Expand/collapse<strong> case actions </strong>sidebar section</td></tr><tr><td><code>⇧ T</code></td><td>Expand/collapse<strong> tasks and closure requirements</strong> sidebar section</td></tr><tr><td><code>⇧ L</code></td><td>Expand/collapse <strong>linked cases </strong>sidebar section</td></tr></tbody></table>

### Creating a case

You can create a case from three different places. 

1. Cases home (from scratch) 
2. Storyboard
3. Records

## Create from scratch

Create a blank case from the cases home and click the `+ New case`. From here, define your preferred case attributes such as case name, priority, and assignees and click `Create case`.

**

## Create from storyboard

Most often, a case will be created from your storyboard as part of a broader story. 

This is a recommended approach as it allows you to automate the end-to-end process that cases are being created for, while capturing the actions and relevant data for auditing purposes. 

To create a case in the storyboard, select the case tile from the Tools section and drag onto the storyboard. Then configure your preferred case details in the right hand builder.

**

### Save a case template

You can also choose to create a case from a saved case template. To create a case template, save your designed case tile via the button `Save as template` at the bottom of the builder. Once you have saved a case template, you can select your pre-configured template when you drag out a case tile via the button to the right of `Configure case`.

**

### Update a case

Please note, the case tile is used for creating cases. To make other changes to cases such as updating, retrieving, or deleting a case, use our API templates. Drag the Tines product from the API template navigator and filter for cases to view the full list of actions available.

**

## Create from records

Cases will almost always be tied to a [record](https://www.tines.com/docs/records-cases/records), but you can also create a case from a record. 

To do this, go to the records section of your team and follow these steps: 

1. Select one or more records you’d like for your case
2. Go to the top of the table and choose ‘add to case’
3. Then choose to add it to an existing or create a new case
4. If you add to existing, you’ll choose from the existing cases
5. If creating a new case, you’ll need to fill in the relevant information about the case
6. You’re all set! 

**

### Case content

Case content is where you build the narrative of an investigation — descriptions, notes, blocks, and comments that capture evidence, decisions, and context.

Every case has three content surfaces:

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 31.934%;"><col style="width: 30.6306%;"><col style="width: 37.4736%;"></colgroup><tbody><tr><th class="TableHeader_t1c5hy0"><span>Surface</span></th><th class="TableHeader_t1c5hy0"><span>Purpose</span></th><th class="TableHeader_t1c5hy0"><span>Persistence</span></th></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Description</strong></span></td><td class="TableCell_t8mlxr3"><span>The primary narrative area at the top of every case. Always visible, always first in the table of contents.</span></td><td class="TableCell_t8mlxr3"><span>Durable — meant to be the source of truth.</span></td></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Case blocks</strong></span></td><td class="TableCell_t8mlxr3"><span>Discrete, reorderable sections below the description. Each block has its own title, color, type, and visibility settings.</span></td><td class="TableCell_t8mlxr3"><span>Durable — structured evidence, checklists, files, linked cases, and more.</span></td></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Comments</strong></span></td><td class="TableCell_t8mlxr3"><span>Real-time discussion in the activity timeline.</span></td><td class="TableCell_t8mlxr3"><span>Durable — but conversational. Decisions from comments should be promoted into the description or a block.</span></td></tr></tbody></table>

All three surfaces share the same rich-text editor powered by CommonMark and GFM formatting, slash commands (`/`), and @mentions.

### How content flows into a case

Content can be authored in several ways:

- **Manually** — type directly into the description, a block, or the comment composer.
- **Via a case template** — pre-populate descriptions and blocks when a case is created.
- **Via story automation** — use the Cases API or action templates to create and update descriptions, blocks, and comments programmatically.
- **Via paste** — paste text, images, or case URLs directly into any rich-text surface.

### Typical workflow

1. A case is created (manually or by a story) and the description is populated with initial context.
2. Blocks are added to organize evidence, remediation steps, timelines, or other structured sections.
3. Teammates collaborate via comments and @mentions in the activity timeline.
4. As the investigation progresses, key decisions from comments are promoted into the description or blocks.
5. The case is exported (PDF) or referenced in dashboards and records.

### Content limits (summary)

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 55.4243%;"><col style="width: 44.5804%;"></colgroup><tbody><tr><th class="TableHeader_t1c5hy0"><span>Limit</span></th><th class="TableHeader_t1c5hy0"><span>Value</span></th></tr><tr><td class="TableCell_t8mlxr3"><span>Description character limit</span></td><td class="TableCell_t8mlxr3"><span>640,000</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>Note block content limit</span></td><td class="TableCell_t8mlxr3"><span>15,000</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>HTML block content limit</span></td><td class="TableCell_t8mlxr3"><span>60,000</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>Comment character limit</span></td><td class="TableCell_t8mlxr3"><span>640,000</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>Block title character limit</span></td><td class="TableCell_t8mlxr3"><span>100</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>Max blocks per case</span></td><td class="TableCell_t8mlxr3"><span>50</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>Max note blocks per case</span></td><td class="TableCell_t8mlxr3"><span>50</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>Max block groups per case</span></td><td class="TableCell_t8mlxr3"><span>15</span></td></tr><tr><td class="TableCell_t8mlxr3"><span>File size limit</span></td><td class="TableCell_t8mlxr3"><span>60 MB</span></td></tr></tbody></table>

For the full list, see [Case limits](https://www.tines.com/docs/limits/)

### What's next

- [Writing and formatting](https://www.tines.com/docs/writing-and-formatting/) learn the editor: slash commands, @mentions, images, code blocks, and CommonMark syntax.
- Structure your case with blocks, block groups, colors, sensitive settings, and the table of contents.
- [Multiplayer collaboration](https://www.tines.com/docs/multiplayer-collaboration/) — collaborate in real time and understand how comments relate to notifications.

#### Writing and formatting

Use CommonMark formatting, slash commands, and @mentions to write rich, structured content inside case descriptions, notes, and comments.

### Where the rich-text editor appears

The same editor is used across all case content surfaces:

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 35.9819%;"><col style="width: 34.1461%;"><col style="width: 30.1073%;"></colgroup><tbody><tr><th class="TableHeader_t1c5hy0"><span>urface</span></th><th class="TableHeader_t1c5hy0"><span>What it's for</span></th><th class="TableHeader_t1c5hy0"><span>Character limit</span></th></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Description</strong></span></td><td class="TableCell_t8mlxr3"><span>Primary narrative; searchable; always pinned to the top of the case.</span></td><td class="TableCell_t8mlxr3"><span>640,000</span></td></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Note blocks</strong></span></td><td class="TableCell_t8mlxr3"><span>Discrete rich-text sections within the block structure.</span></td><td class="TableCell_t8mlxr3"><span>15,000</span></td></tr><tr><td class="TableCell_t8mlxr3"><span><strong>HTML blocks</strong></span></td><td class="TableCell_t8mlxr3"><span>Rendered HTML content within the block structure.</span></td><td class="TableCell_t8mlxr3"><span>60,000</span></td></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Comments</strong></span></td><td class="TableCell_t8mlxr3"><span>Activity timeline discussion. Sticky composer at the bottom of the panel.</span></td><td class="TableCell_t8mlxr3"><span>640,000</span></td></tr></tbody></table>

> **TIP:** Headings you add inside the description appear in the table of contents sidebar, making long descriptions scannable.

---

### Slash commands (`/`)

Type `/` anywhere in the editor to open the command menu. The available commands are:

| Command | Description |
| --- | --- |
| **Heading** (1–6) | Insert a heading. Headings in the description appear in the table of contents. |
| **Bullet list** | Unordered list. |
| **Ordered list** | Numbered list. |
| **Task list** | Checklist with toggleable checkboxes. |
| **Callout** | Highlighted callout block. Once inside a callout, `/` also offers callout type variants (info, warning, success, error). |
| **Code block** | Fenced code block for logs, IOCs, CLI output, or scripts. |
| **Quote** | Block quote for referenced material. |
| **Table** | Insert a table. Additional `/` commands for row and column operations are available when the cursor is inside a table. |
| **Image** | Insert an image from file or URL. |
| **Mermaid** | Insert a Mermaid diagram rendered inline. Useful for flowcharts, sequence diagrams, or timelines. |

> **INFO:** Horizontal rules can be created ( ---) via markdown typing shortcuts but are not listed in the slash command menu.

---

### Mentions (`@`)

Type `@` to open the mention picker. Mentions are **not limited to users** — you can mention a wide range of Tines entities:

| Mentionable entity | What happens |
| --- | --- |
| **User** | Creates a mention pill linking to the user's profile. Subscribes them to case notifications. |
| **Case** | Deep link to another case. |
| **Story** | Deep link to a story. |
| **Page** | Deep link to a page. |
| **Action** | Deep link to an action within a story. |
| **Resource** | Deep link to a resource. |
| **Webhook** | Deep link to a webhook. |
| **Case input** | Reference to a case input. |
| **Case button** | Reference to a case button (case action). |
| **Case file** | Reference to a file attached to the case. |
| **Case block** | Reference to a block within the case. |
| **Record type** | Reference to a record type. |

> **TIP:** @mentioning a **user** subscribes them to the case and triggers a notification. Mentioning non-user entities creates a navigable link but does not trigger notifications. Use user mentions intentionally to keep notification noise low.

### Images and uploads

You can add images to any rich-text surface:

- **Paste from clipboard** — take a screenshot and paste (`Ctrl/Cmd + V`) directly into the editor. The image renders inline immediately.
- **Drag and drop** — drag an image file from your desktop into the editor.
- **Slash command** — type `/image` to insert via the command menu.

Images are uploaded as case files and are subject to the **60 MB file size limit** per file.

> **TIP:** Inline images are immediately visible to all case viewers — prefer pasting screenshots over attaching files when the visual context matters.

### Code blocks

Use code blocks for preserving formatting on structured content like log entries, IOC lists, JSON payloads, or CLI output.

- **Via slash command:** type `/code` and select **Code block**.
- **Via markdown shortcut:** type ````` and press Enter.

Content inside a code block is rendered in a monospace font and is not parsed for formatting.

### CommonMark and GFM formatting

The editor supports [CommonMark](https://commonmark.org/) and GitHub Flavored Markdown (GFM) syntax. You can type markdown directly or use the toolbar and slash commands.

| Format | Syntax | Result |
| --- | --- | --- |
| Bold | `**text**` | **text** |
| Italic | `*text*` | *text* |
| Strikethrough | `~text~` | ~text~ |
| Inline code | `` `code` `` | `code` |
| Link | `[text](url)` | [text](https://hq.tines.io/workbench/url) |
| Heading | `# H1` through `### H3` | Heading levels 1–3 |
| Bullet list | `- item` | Unordered list |
| Ordered list | `1. item` | Numbered list |
| k list | `- [ ] item` | Checkbox list |
| Block quote | `> text` | Block quote |
| Code block | ` ``` ` | Fenced code block |

> **TIP:** When working with the Cases API or story actions, content is stored and returned as raw CommonMark markup. Familiarity with the syntax is helpful when building automations that populate case content.

### Formulas in case content

When formulas are enabled on a rich-text surface, you can embed Tines formula expressions directly in case content. This is primarily relevant when case content is populated by story automations — allowing dynamic values to be resolved at write time.

### Best practices

- **Use headings in descriptions** to create a scannable structure — they populate the table of contents.
- **Paste images inline** rather than attaching files — they're immediately visible to all viewers.
- **Use @mentions intentionally** — mentioning a user subscribes them and triggers notifications.
- **Use code blocks for IOCs, logs, and structured data** to preserve formatting and readability.
- **Promote decisions from comments** into the description or a note block so they're durable and findable.

#### Multiplayer collaboration

Edit a case description or note alongside your teammates in real time. See who is actively viewing or editing a case note, their draft changes, and each editor's cursor position.

Real-time collaborative editing supports faster triage and review sessions within [Cases](https://hq.tines.io/docs/cases/) — multiple editors can contribute to the same case description or note without overwriting each other's work.

## How it works

When two or more users edit the case **description** or a case **note**, they see each other's drafted changes in real time.

### View a case simultaneously

When you view a case at the same time as another user, a collaboration indicator appears at the top of the case. This includes the collaborator's information but does *not* indicate the specific case content they are viewing.

![](https://www.datocms-assets.com/55802/1776967330-multiplayer-notification-of-who-s-viewing.png)

### Edit case content simultaneously

When another user is actively editing content in the case, a collaboration indicator appears at the top of the case showing:

- The user who is currently editing
- The tite of the content they are editing

Select the user's avatar to jump directly to the content they are editing. This automatically opens the description or note block in edit mode.

**

When scrolling through a case, a collaboration indicator is shown directly on any notes actively being updated. Simply edit the description or note to see their real-time changes in the draft.

![](https://www.datocms-assets.com/55802/1776968910-multiplayer-indication-of-editing.png)

## Save your work

Select **Done** to save your changes to the case description or note.

Collaborators can continue drafting changes after a note has been saved — their updates are stored as a draft until they subsequently select **Done** or **Cancel**.

- If you navigate away without saving or cancelling, the draft is preserved for other collaborators still editing.
- This draft is preserved for up to 24 hours after the last change to the draft.
- If you select **Cancel** and are the sole editor of a note, your draft is discarded immediately.

> **INFO:** If the connection drops briefly, you may see a *Reconnecting* status. If the connection can't be restored after multiple attempts, store any changes in a separate location until you're back online.

## Reset to saved version

Click the **Reset to saved version** button to revert to the last saved content in the note. This discards *all* unsaved changes for *every* collaborator in the session.

![](https://www.datocms-assets.com/55802/1776969169-revert-icon.png)

Selecting the reset button opens a confirmation dialog before changes are discarded. 

- Use this when the draft has diverged and you need a clean starting point. 
- If you're not certain you want to discard changes, copy any text you need before resetting.

## Connections and session

Multiplayer uses a concept called "sessions" to facilitate the multiplayer experience. When editing a note, a new session is created. As collaborators edit the same note, each users joins the session for that note. These sessions require a consistent internet connection to display live updates. 

- **Inactivity** – if no changes occur in the editor for approximately 10 minutes, the real-time session may end and the UI displays *Session expired*. Begin typing again to automatically reconnect.
- **Drafts** – When changes are made but not saved, the draft is stored as part of the note's session. When the session ends (by expired time or all users clicking cancel) those draft changes are discarded.
- **Permissions** – you can only join a real-time session if you have edit access to that part of the case. Users with read-only permissions can't interact with the real-time editing experience.

## Limitation of multiplayer

- Multiplayer collaboration is only supported with the case description and note content when using the rich text editor in a case. Other case activity, comments, fields, links, metadata, status, tags, title, etc are updated synchonously.

## Best practices

- **Save frequently.**  Multiplayer editing doesn't replace explicit saves — treat **Done** as your checkpoint.
- **Communicate before resetting.** Resetting to the saved version affects everyone. The confirmation dialog helps prevent accidental resets, but let collaborators know before you discard the current draft.

### Case fields

Case fields let you capture custom, structured data within your cases. Data like ticket IDs, affected systems, or investigation outcomes. These fields extend beyond the built-in case properties like name, status, and priority.

### How case fields fit into Cases

Case fields follow a define-once, use-everywhere pattern:

1. **Define** fields in your team's case settings – choose a type, set validation rules, and mark fields as sensitive if needed.
2. **Attach** fields to cases – add case fields and field groups within a case and case template, or automatically attach fields to cases via story actions.
3. **Interact** with field values – view and update case fields in the cases sidebar or wherever they are mentioned within a case, via story automation, or dynamically within a page.
4. **Report** on field data – filter the case list and board by field values, or report on case field data within dashboard charts.

### Key concepts

- **Field types** – every field has a type (text, number, timestamp, or boolean) chosen at creation. The type determines the input control and cannot be changed later.
- **Validation** – text fields optionally support validation: a fixed list of allowed values or a regex pattern. Other field types do not support validation.
- **Sensitive fields** – fields [marked as sensitive are redacted](https://www.tines.com/docs/sensitive-case-fields/) for users without the *View sensitive information* permission, including in the activity timeline and `@` mentions within the case body. 
- **Groups** – fields can be organized into named, collapsible groups (displayed as folders in the case sidebar) keeping related fields together. Groups are a convenient way to interact with a large number of related fields in cases, search, and dashboards.
- `**@**`** mentions** – reference a field's live value directly in case descriptions, notes, and comments by typing `@` and selecting the field name. Mentions allow you to update the field value inline, continuing through the case uninterrupted.

> **NOTE:** When working with case fields in a story or via API, fields may be referred to as "inputs".

### Define and configure fields

Fields are created and managed in your team's case settings. Fields defined within a team *cannot* be seen or used by other teams. From here you can:

- Add, rename, and delete fields
- Configure validation rules (fixed list or regex)
- Mark fields as sensitive — see [Sensitive case fields](https://www.tines.com/docs/sensitive-case-fields/)
- Organize fields into groups

![](https://www.datocms-assets.com/55802/1775256961-doc-case-fields-settings.png)

### Interact with fields within a story or via API

Once defined, fields can be further leveraged with using the **Create case** story tile, our story templates, or via the API.

- Create case story tile requires that you select a team to proactively load which fields can be added to the case.
- When using [story templates](https://www.tines.com/docs/templates/) or the API, they may refer to case fields as `case inputs`
- `Load case field IDs, names, and values quickly in a formula`

> **NOTE:** Reference \`INFO.cases.inputs\` to quickly reference a specific case field ID, name, or predefined values.



### Use fields within a case

Once defined, fields can be added to individual cases or case templates to capture case-specific data. You can also reference field values inline using `@` mentions.

- Add, edit, and remove field values
- Mention fields in descriptions, notes, and comments

![](https://www.datocms-assets.com/55802/1775256897-doc-case-fields-in-case-example.png)

[](https://demo.arcade.software/EFzjj6X99dmOQl0ON5ca?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

### Automate and report on fields

- **Story actions** – set field values automatically when creating or updating cases from a story.
- **Search and filtering** – filter cases by field values, combine conditions with AND/OR logic, and save filtered views.
- **Dashboards** – use case fields as a data source in dashboard charts to visualize trends across cases. See [Dashboards](https://www.tines.com/docs/dashboards/)

![](https://www.datocms-assets.com/55802/1775259057-doc-case-fields-dashboard.png)

### Permissions

Access to case fields is controlled by your roles and permissions. 

Creating, updating, and deleting field definitions requires *Case field* permissions on custom roles, or the *Manage cases* permission.

Viewing sensitive field values requires the *View sensitive information* permission.

### Limits

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 59.2466%;"><col style="width: 40.8598%;"></colgroup><tbody><tr><th>Resource</th><th>Limit</th></tr><tr><td>Fields defined per team</td><td>1,000</td></tr><tr><td>Fields added to a case</td><td>100</td></tr><tr><td>Fixed list options for a field</td><td>75</td></tr><tr><td>Text field type length</td><td>1,000 characters</td></tr></tbody></table>

### Related pages

- [Case limits](https://www.tines.com/docs/limits/)
- [Navigate a case](https://www.tines.com/docs/layout/)
- [Components of a case](https://www.tines.com/docs/components/)

#### Sensitive case fields

Sensitive case fields let you restrict visibility of specific field values to authorized users — useful for protecting PII, legal data, or confidential details during an investigation.

### When to use sensitive fields

Mark a case field as sensitive when its value should only be visible to a subset of users. For example, if a case tracks an HR investigation, you might mark fields containing employee names or salary data as sensitive so only authorized investigators can see them.

### How it works

Sensitive case fields combine two concepts:

1. **Assign the permission:** the `View sensitive information` permission controls who can see the values of sensitive fields. 
2. **Mark a field as sensitive: **any case field can be marked as sensitive by a user able to manage case fields. This flags the field for restricted visibility across the team.

When both are in place, the platform dynamically evaluates each user's access at the point they encounter a sensitive field — on the case itself, in search results, in activity logs, in exports, and in mentions. Users without the permission see that the field exists but never see its value.

![](https://www.datocms-assets.com/55802/1775172417-doc-fields-sensitivefieldexample.jpg)

This means you can have the same field (e.g., "Employee ID") behave differently depending on who is viewing the case.

### Grant access to sensitive data

Whether a user can view the contents of a sensitive field depends on whether they have the `View sensitive information` permission in their role.

- The **Team admin** role is granted the permission by default.
- To grant non-admin users access to sensitive fields, your tenant also needs the **custom roles** feature enabled which is a part of *Enterprise tenant management *bundle.
  
  - You can add the **View sensitive information** permission to any custom role. 
  - The permission is additive — it doesn't grant the ability to read or write all case fields inherently. 

### Set up a sensitive field

1. Navigate to **Cases** > **Settings **> **Fields**
2. Select an existing field's options and enable the **Sensitive** toggle (all field types can be marked as sensitive).
3. Save your changes.

> **TIP:** You can mark a field as sensitive when creating case inputs via the [Tines API](https://www.tines.com/api/cases/case_inputs/create/) by setting the `sensitive` parameter to `true`.

### How sensitive fields behave

#### What you see

Users without the `View sensitive information` permission can still see:

- **Field names** — the field appears on the case, but the value is replaced with a redacted placeholder
- **Activity entries** — `field_updated` entries in the activity list show the field name only (e.g., "SSN: ") with no value
- **Mentions** — `@` mentions of a sensitive field within a note, description, or comment displays the field name but hides the value
- **PDF and CSV exports** — sensitive fields show "Sensitive value not accessible" in place of the value

#### What you can do

Without the permission, users are blocked from:

- **Editing** a sensitive field, even if they can edit non-sensitive fields on the same case
- **Adding** a sensitive field to a case — sensitive fields are hidden from the **Add field** menu
- **Deleting** a sensitive field value from a case
- **Searching** by sensitive field values — the platform silently drops sensitive field filters from the case list search queries

With the permission, users can interact with sensitive case fields the same way they would with any other field.

#### What happens outside of cases

- **Workbench always excludes sensitive fields from context.** Regardless of the user's permissions, Workbench cannot see or reference sensitive field values.
- **Story-triggered actions and webhooks may include raw values.** When a story action or webhook fires from a case, it may include sensitive field values because it executes outside a user's permission context. Plan your downstream stories accordingly.
- **Dashboards display values in clear text.** Sensitive fields are not redacted in dashboards. Limit dashboard access if sensitive data is included.

### Things to keep in mind

- **Sensitive fields are not encrypted differently.** Marking a field as sensitive controls *who can view the value* — it does not change how Tines stores the data. Values are stored the same way as non-sensitive fields.
- **Case note sensitivity is unrelated.** Cases also support marking individual notes as sensitive, which controls whether the note is included in PDF exports. This is a separate concept from sensitive case *fields* and uses a different mechanism. The two do not interact.
- **Avoid deleting sensitive fields.** Once a sensitive field is deleted, the platform can no longer identify it as sensitive. Any historical activity entries that referenced that field will display their original, unredacted values to all users.
- **Export and import preserve sensitivity.** When you export a case template, the `sensitive` attribute is included in the field definitions. Importing the template on another tenant recreates the field with the same sensitivity setting.

### Best practices

- **Audit your sensitive fields regularly.** Review which fields are marked as sensitive to ensure the list reflects current requirements.
- **Pair with custom roles.** Use the **View sensitive information** permission alongside tightly scoped custom roles so only the right people can see protected data.
- **Be cautious with dashboards and webhooks.** Since these surfaces display raw values, avoid including sensitive fields in dashboards visible to broad audiences, and review story logic that processes sensitive data.

### Related pages

- [Components of a case](https://www.tines.com/docs/components/)
- [Custom roles](https://www.tines.com/docs/custom-roles/)
- [Tines API – case inputs](https://hq.tines.io/docs/api/)

### Case groups

A case group is a subset of a [Tines team](#datocms-record-122391647).

## Using Groups

Case groups provide a way to segment cases, case views, and dashboards.

1. Users within a group are limited to viewing that group's cases, case views, and dashboards
2. Users can be members of multiple groups with different permissions for each group. 
3. Cases can move between groups.

For example, in a SOC, the **Incident Response **team has its own users, credentials, resources, and stories. Within this team, three groups carry out distinct functions: 

- **Tier 1**:* Incident triage*
- **Tier 2**:* Investigations*
- **Tier 3: ***Threat hunting*

![](https://www.datocms-assets.com/55802/1731697936-case-groups-1.png)

## Managing groups

[Team Admins](https://www.tines.com/docs/teams/) and [Tenant Owners](https://www.tines.com/docs/user-administration/) create and manage groups directly within the team. Tenant Owners are automatically members of all groups within a team.

> **INFO:** Only three groups can be created per team. If you need to create more than three groups, please notify your Tines rep.

### Viewing group membership 

To view membership, navigate to "**Cases**" and open the group selection menu. Select the group you'd like to view membership for. 

Re-open the team selection menu and navigate to **Manage** > **Members.** A list of members and their roles are displayed. 

![](https://www.datocms-assets.com/55802/1732315344-case-membership-management-6.png)

### Managing group membership

To manage group membership, first select the desired group to manage from the team selection menu. Next, re-open the selection menu and click **Manage** > **Members**

> **INFO:** A user can be member of a team, a group, or both.

#### Assigning team members to a group

Users who are part of a team can quickly be assigned to one or more groups.

![](https://www.datocms-assets.com/55802/1732314505-case-membership-management-4.png)

#### Inviting users to a specific group

In some cases, you may want to invite a user to a group. To add other members to your group, select **Manage** > **Members **> **Invite**. 



### Managing roles and permissions within a group

To manage roles for group members, opent the group selection menu and click **Manage** > **Members**. Each group member will have an additional configuration option which allows you to specify their role.

The role selected only applies to the cases, case views, and dashboards within that specific group.

![](https://www.datocms-assets.com/55802/1731706212-case-membership-management-2.png)

#### Managing permissions 

Permissions are managed by selecting the **Administration menu** > **Users. **Please see [Teams](#datocms-record-122391647) to learn more about roles and their permissions.

> **TIP:** Permissions assigned at the team level take precedence over permissions assigned at the group level.

For instance, a team member holding the `Case Manager` role, who is also part of a group with the `Viewer` role, will manage the group's cases as a Case Manager.

<table border="1" style="border-collapse: collapse;"><tbody><tr><td><strong>Permission/Team Role</strong></td><td><strong>Team Admin</strong></td><td><strong>Editor</strong></td><td><strong>Viewer</strong></td><td><strong>Case manager</strong></td><td><strong>Dashboard manager</strong></td></tr><tr><td>Read cases</td><td>x</td><td>x</td><td>x</td><td>x</td><td></td></tr><tr><td>Create / update cases</td><td>x</td><td>x</td><td></td><td>x</td><td></td></tr><tr><td>Create / update records</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Create / update dashboards</td><td>x</td><td>x</td><td></td><td>x</td><td>x</td></tr><tr><td>Manage team members (add / remove)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Share objects (stories, resources, credentials, templates)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Move objects (stories, resources, credentials)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Delete objects (stories, resources, credentials, events, records)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Delete templates</td><td>x</td><td>x</td><td></td><td></td><td></td></tr></tbody></table>

## Assigning cases to groups

When security and IT tools generate events, Tines processes these events and associates them to a new or existing case.

In some situations, a case may need to be escalated or moved to another group.

Moving a case can occur in three ways:

1. The group is explicitly defined in initial conditions of the case-creation story object
2. The case is escalated to another group using a case action built in your story
3. The case is manually moved to another group

> **WARNING:** Cases cannot be moved between Teams.

### Automatically assign a group at case creation

When you define a "case" object [within the storyboard](https://www.tines.com/docs/creating-a-case/), you can select the group to which the case should automatically be assigned. 

![](https://www.datocms-assets.com/55802/1731701015-case-assignment-via-story.png)

### Escalate using a case action

Cases include custom quick-response buttons called `Case Actions,` which trigger a webhook help drive the case to resolution.

When you configure a case template or the case directly within the storyboard, you can reference a webhook story object for a case action. 

You must first create this webhook object [within the storyboard](https://www.tines.com/docs/creating-a-case/) and build a story that specifies which group the case should be assigned when the webhook is triggered.

![](https://www.datocms-assets.com/55802/1731701015-case-assignment-via-story.png)

### Move a case to another group

If a case needs to be escalated, you can manually move it between groups. Analysts will only see cases and dashboards associated with their group.

#### From the case list

When viewing cases on the case list, one or more selected cases can be moved to another group within the team.

![](https://www.datocms-assets.com/55802/1731703552-case-assignment-via-move.png)

#### When viewing a case

When viewing an individual case, select the `+` or "assignee" button. From there you can select which group the case should be transfered to.

![](https://www.datocms-assets.com/55802/1732312621-case-assignment-via-move-2.png)

### Case notifications

## How to use case notifications

A **case notification** is generated whenever a case is created or updated. These notifications can be used to trigger a story, where the notification event includes the change details.

> **TIP:**
> For example:
> 
> -   Send a message [via Slack or Teams](https://www.tines.com/library/stories/1197267/?name=send-tines-case-notifications-to-subscribers-in-slack-or-teams) whenever a case is updated
> -   Keep [Zendesk and Tines tickets synchronized](https://www.tines.com/library/stories/1186198/?name=track-issues-across-tines-and-zendesk&redirected-from=%2Flibrary%2F%3Fview%3Dall&s=notification+case) as changes are made

## Configuring a case notification

Case notifications work in tandem with a [webhook action](https://www.tines.com/docs/webhook/) from your story board. 

To establish a subscription, you must create a webhook action in a story, and then paste the URL to that webhook in your team's case settings.

### Creating a webhook subscription

1. Create a webhook action in the target story
2. Copy the webhooks URL (e.g. `http://<tenant>.tines.com/webhooks/<path>/<secret>`)
3. Navigate to the desired Team
4. Navigate to `Cases` > `Case settings` > `Notifications` and click the `Add` button
5. Paste the webhook URL into the input and save

Newly created webhooks recieve all case notifications, however, you can configure which notifications the webhook receives from the dropdown.

**

### Viewing the webhook subscription

Select the `Go to webhook` option from the more options dropdown to take you to the story that contains the webhook action.

![](https://www.datocms-assets.com/55802/1765971953-screenshot-2025-12-17-at-11-45-45.png)

## Receiving case notifications

Now that your webhook subscription is configured, configured case updates will be sent to that webhook. If subsequent actions are linked to the webhook, they will be run every time a case change occurs.

> **NOTE:** You can configure up to 5 webhook notifications per team.

### Notification triggers

Nearly all changes to a case will result in a case notification. When viewing the event details from a case notification, the `activity` object will indicate what has changed.

![](https://www.datocms-assets.com/55802/1755035279-docs-casenotification-eventtype.png)

### Supported activity types

A webhook event will occur for [any audit log events](https://www.tines.com/api/audit-logs/) related to cases, including case creation, comments, file attachments, tag updates, and case updates. Specifically, the following activity will generate an event:

> **INFO:**
> In addition to what's listed in the "*Activity details"* column below, each event will always include:
> 
> -   Datetime of the change
> -   Object describing the user who caused the change

<!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}-->

| **Category** | **Activity type** | **Description** | **Activity details** |
| --- | --- | --- | --- |
| **Assignee** | assigned | A user was assigned to the case | Assigneed user's ID |
|  | unassigned | A user was unassigned from the case | Unassigned user's ID |
| **Attachments** | file\_attached | A file was uploaded to a file block or in an empty comment | Filename &  
File URL |
|  | file\_attached\_and\_commented | A file was uploaded in a comment with text | Comment text,  
Filename, &  
File URL |
|  | file\_deleted | A file was deleted from a comment or file block | *No additional details* |
|  | file\_pasted | An image was pasted inline to the case description | *No additional details* |
|  | file\_pasted | An image was pasted inline to a note block | Block title &  
Block ID |
| **Case details** | team\_updated | The case was moved between groups within a team | Team name |
|  | field\_updated | A case field was updated | Field name: Field value |
|  | title\_updated | The title of the case was updated | Case title |
| **Case links** | linked\_case\_added | The case was linked to another case | *Two events are generated, one for each case involved:*  
Linked case ID |
|  | linked\_case\_removed | The case was unlinked from another case | *Two events are generated, one for each case involved:*  
Unlinked case ID |
| **Comments** | commented | A comment was added to the case | Comment text |
|  | deleted\_comment | A comment was deleted from the case | Comment text |
| **Content** | description\_updated | The case's "description" input was updated | Description text |
|  | note\_added | A note, table, or code snippet case block was added | Note title &  
Note block ID |
|  | note\_deleted | A note, table, or code snippet case block was deleted | Note title &  
Note block ID |
|  | note\_updated | A note, table, or code snippet case block was updated | Note title &  
Note block ID |
| **Metadata** | metadata\_updated | The metadata object was updated | Metadata object |
| **New case** | created | A new case was created | Case ID |
| **Records** | record\_result\_set\_added | A record was associated with the case | 
Record ID &  
CSV of the record's values

 |
|  | record\_result\_set\_removed | A record weas removed from the case | Record ID &  
CSV of record's values |
| **Severity / SLA** | severity\_updated | The priority (info, low, medium, high, critical) was changed | Updated severity ("priority") value |
|  | sla\_warning | The SLA was is 80% of allotted time | SLA type: response, resolution ("completed") |
|  | sla\_exceeded | The SLA was exceeded | SLA type: response, resolution ("completed") |
| **Status** | status\_updated | The status group was updated | Status: open, closed |
|  | sub\_status\_updated | The sub-status was updated | *This may result in a "status\_updated" event also being generated:*  
Sub-status |
| **Tags** | tags\_added | A tag was added to the case | Tag name |
|  | tags\_removed | A tag was removed fromt the case | Tag name |
| **Tasks** | checklist\_item\_completed | A checklist item in the case description was checked | Checked item name |
|  | checklist\_item\_incomplete | A checklist item in the case description was unchecked | Checked item name |

## Routing case notifications to specific stories

In some cases, you'd like to route specific notifications to relevant stories. Use the trigger action to evaluate the `activity_type` and/or `value` of the case notification and route the event to a [send-to-story ](https://www.tines.com/docs/send-to-story/)(or other pathway) accordingly.

![](https://www.datocms-assets.com/55802/1755040879-case-notification-distributor-storyboardview.png)

## Updating the version of your webhook

When Tines updated our [cases API to v2.0](https://www.tines.com/whats-new/cases-api-v2/), the resulting case notification payload also changed. 

If your webhook action was created prior to **April 30th, 2024** then be sure to make the appropriate payload key changes to any stories consuming events from the case notifications webhook:

- The `team_case` key is now `case` 
- Within this `case`  object the `linked_team_cases` key is now `linked_cases`
- The  `team_case_action `  key (an item of timeline activity on the case) is now `activity` 
- Within this `activity` object the `action`  key is now `activity_type`

Once these changes have been made the case webhook notification version can be updated by selecting the checkbox to update the webhook version and saving the changes within the team notification settings.

![](https://www.datocms-assets.com/55802/1765971897-screenshot-2025-12-17-at-11-44-46.png)

### Case tasks

## Introducing case tasks

Case tasks are a great way to organize and report on requirements within a case. They can be used to represent anything that should be completed when interacting with a case. 

- Create tasks and view a list of tasks from within the case sidebar
- Assign tasks to one or more users within a team or case group
- Mark tasks complete or incomplete
- Filter the list of cases based on open task assignments
- Leverage case notifications to trigger workflows from task activities
- Require tasks be completed as part of case closure requirements.



## Moving from checkboxes to tasks

Historically, checkboxes within a case's description was categorized as a to-do or task. These checkboxes and their state would be tallied to determine how many tasks were complete.

The aforementioned checkboxes and case tasks do not have a relationship. You can can continue to use checkboxes to represent a to-do, but they won't be reportable, filterable, or centrally managed like case tasks.

> **TIP:** We recommend updating your templates and stories to leverage case tasks instead of checkboxes. Doing so offers enhancements such as reporting, notifications, and centralized management.

## Working with tasks

[](https://demo.arcade.software/WN9PxSigTyYhzytOs9Ho?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

### Adding a task

You can add up to 50 tasks per case. Task titles are simple text and do not support Markdown formatting.

![](https://www.datocms-assets.com/55802/1759158726-tasks-add-new-ui.png)

- **In an existing case, **click the tasks button on the cases toolbar, then the "add task" button.
- **When creating a case from scratch. **add tasks before the case is assigned.
- **Within a case template,** you can define a set of tasks that will appear in each case created from the template.
- **Through story automation,** use the create case story action with a template, or the [Tines tasks API](https://tines.com/api/cases/tasks/create) to interact with tasks programatically.

### Updating a task

Once a task is added to a case, you can update it via the UI or within stories via API.

- **Assign one or more users** to a task. The user(s) must be within the team or case group of the target case. Assignees will reveive a notification and are automatically subscribed to the case.
- **Edit details** to update the name of a task (up to 100 characters).
- **Review timestamps **on the specific task or within the activity feed. This will update when the task was initially created or when a task was marked "completed".

### Completing a task

A task's status can be toggled between "incomplete" and "complete". 

- The task counter shown in the case header represents the number of completed tasks vs. total tasks.
- The task counter in the toolbar shows the total number of tasks on a case.

### Deleting a task

Deleting a task will remove it from the case entirely. Tasks can NOT be restored once deleted.

### Referencing a task

When building a case template, you can refer to a task using the `team_case.tasks` path. This is useful especially for requiring tasks as part of case closure conditions.

## Reporting on tasks

### Case notifications

Interacting with a task will generate a [case notification](https://www.tines.com/docs/case-notifications/). The following activities can occur: `task_created`, `task_updated` when the name is edited, `task_assigned`, `task_unassigned`, `task completed`, and `task_incomplete`.

These activities will also appear in a case's activity timeline.

![](https://www.datocms-assets.com/55802/1759158850-tasks-case-notification.png)

### Number of tasks remaining in a case

The task counter shown in the case header represents the number of completed tasks vs. total tasks.

![](https://www.datocms-assets.com/55802/1759158434-tasks-header.png)

Add the "Tasks" column when filtering cases to see the number of complete tasks vs total in bulk.

![](https://www.datocms-assets.com/55802/1759158629-tasks-columns.png)

### Filtering cases by open tasks

- Use the "My open tasks" filter to quickly find cases where you have work to do.

![]()

## Roles & permissions

#### Default roles

- **Team admin, Editor, and Case manager roles** have all task permissions listed below
- **Viewer role** can only view tasks

#### Custom roles

You can select the following permissions when defining a [custom role](https://www.tines.com/docs/custom-roles/):

<table border="1" style="border-collapse: collapse;"><tbody><tr><td><strong>Permission</strong></td><td><strong>Description</strong></td></tr><tr><td>Assign tasks</td><td>Assign a task to one or more users within the Team or case group</td></tr><tr><td>Complete tasks</td><td>Mark tasks complete/incomplete</td></tr><tr><td>Create tasks</td><td>Add tasks to a case, case template, or 'create case' story action</td></tr><tr><td>Delete tasks</td><td>Delete tasks from a case</td></tr><tr><td>Edit tasks</td><td>Change the name of an existing task</td></tr><tr><td>View tasks</td><td>View tasks within a case</td></tr></tbody></table>

## Additional information

- Find the Tasks API documentation at [tines.com/api/cases/tasks/create](https://www.tines.com/api/cases/tasks/create)

### Case limits

## Cases

Maximum case limits per team:

| Type | Limit |
| --- | --- |
| Number of cases | No limit |
| 
Number of case templates

 | 1,000 |
| Number of cases you can bulk update at once | 50 |
| Number of saved case views | 100 |
| 

Case retention period

*Includes case components such as the case details, decription, fields, metadata, comments, notes, and attachments. See [record limits](https://www.tines.com/docs/records-cases/records/limits/) at the end of this article for limits on attached records.*

 | Cases are kept for the duration of the customer’s active license. |

## Case team settings

Maximum case setting limits per team:

| Type | 
Limit

 |
| --- | --- |
| Custom statuses | 10 |
| Case fields | 1,000 |
| Validations per case field | 75 |

## Case primitives

Maximum primitive limits per case:

| Type | Limit |
| --- | --- |
| Assignees per case | 10 |
| Case action buttons per case | 25 |
| Case fields per case | 100 |
| Case field groups per case | 100 |
| Closure conditions | 25 |
| Linked cases per case | 50 |
| Metadata keys per case | 100 |
| Records attached to a case | 1,500 |
| Tags per case | 50 |
| Tasks per case | 50 |

## Case blocks

Maximum case block limits per case:

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Type</td><td>Limit</td></tr><tr><td>Content blocks of any type (Note, HTML, File, Table, Code)</td><td>50</td></tr><tr><td>Case block groups</td><td>15</td></tr><tr><td>Case blocks per group</td><td>50</td></tr></tbody></table>

## Files

Maximum file limits per case:

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Type</td><td>Limit</td></tr><tr><td>Case file types</td><td>csv, doc, docx, eml, gif, gzip, html, jpeg, jpg, json, mov, mp4, pdf, png, ppt, pptx, svg, txt, xhtml, xml, xtar, xls, xlsx, webm, zip, 7z</td></tr><tr><td>Maximum file size</td><td><p><span style="background-color: transparent; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-ligatures: inherit; font-variant-caps: inherit; font-weight: inherit; color: var(--color); letter-spacing: 0px;">60 MB</span></p><p><em><span style="background-color: transparent; color: var(--color); font-family: inherit; font-size: inherit; font-variant-ligatures: inherit; font-variant-caps: inherit; font-weight: inherit; letter-spacing: 0px;">If you need to share a larger file please link to it from a file hosting service.</span></em></p></td></tr><tr><td>Number of files per case block</td><td><span style="background-color: transparent; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-ligatures: inherit; font-variant-caps: inherit; font-weight: inherit; color: var(--color); letter-spacing: 0px;">50</span></td></tr></tbody></table>

## Text

Maximum text limits per case:

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>Type</td><td>Limit</td></tr><tr><td>Comment length</td><td>640,000 characters (per comment)</td></tr><tr><td>Description length</td><td>640,000 characters</td></tr><tr><td>Case field value length</td><td>1,000 characters</td></tr><tr><td>Case metadata value length</td><td>1,024 characters</td></tr><tr><td>Case note title length</td><td>100 characters</td></tr><tr><td>Case note content length</td><td>15,000 characters</td></tr><tr><td>Case task title length</td><td>100 characters</td></tr></tbody></table>

## Additional limits

For more information on the limitations around records, please see [Records limits](https://www.tines.com/docs/limits/). 

For more information on the limitations around dashboard charts, please see [Dashboard limits](https://www.tines.com/docs/limits/).

## Records

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

> **INFO:** Records and cases are available on version 17.20 of self-hosted Tines.

[Records](https://explained.tines.com/en/articles/7971187-introduction-to-records) are a way to introduce structured data into your team. 

[*Explore examples of records in our library*](https://www.tines.com/library/cases-records)*. *

## Capture a record

Drag a record onto the storyboard from the ‘tools’ menu. Then connect it to the upstream and/or downstream action. 

**

This tells the story to capture data from the incoming action before proceeding to the subsequent action(s). Through the build pane on the right-hand side of your screen, you’ll name what you’re capturing and assign the **record type**. 

If you or a member of your team already created record types, you can choose the type from the ‘select record type’ dropdown. 

If no record types exist, you’ll need to select the option **`Create new`** record.

### Create a new record type

Now that you are capturing data, you need to tell the system how you want the data stored. This is done through the **record type**. The record type is how you define the data and how it should be stored. 

This opens a module like below. 

**

By default, the timestamp and story name is always captured. 

> **TIP:** Always use a unique, descriptive name for the record on the storyboard.

You will add fields for what you’d like captured. Today, the field result type options are text, number, true/false, timestamp or a list of fixed text values. 

**

> **NOTE:**
> For example:  
>   
> 
> If your story is conducting IOC analysis, you might want to keep a record of URLScan outputs to monitor trends. You could add fields to keep a record of the different URL outputs, such as: the URL analyzed, the URL ID, whether it has verdicts, whether it was identified as malicious, and the URLScan score.

Once you define the record type, you will click `save` and it will be automatically selected as the record type for the record capture. 

Now, you need to define the incoming data. 

### Define record type data

Since the incoming event data structure varies from one story to another, you need to define the incoming data for each record field. Before starting, ensure the capture record tool is connected to an incoming action.

1. Choose the record type from the drop-down list
2. Then, for each field reference the relevant data from the incoming action
3. Test the record by clicking `**run**` or **`test`**
4. When the run is complete, choose **`view record results`** to see the output on the record table

While running or testing the record tool, you may encounter incoming data type that does not match the defined record field type. When that happens:

1. If a record field is defined as a “number” but the incoming data is a “text”, empty data (essentially `null`) will be recorded, and you’ll see an error in the logs.
2. If a record field is defined as a “text” but the incoming data is a “number”, the value will be recorded as a “text”, e.g. `"10"`.
3. A record field defined as a “true/false” (essentially a boolean) can only record data that is `true` or `false`. If the incoming data is `0`, `no`, or `false`, it will be recorded as `false`. If the incoming data is `1`, `yes`, or `true`, it will be recorded as `true`. 
4. If a record field is defined as a “timestamp” but the incoming data is not a date time, empty data will be recorded and you'll see an error in the logs. A “timestamp”, regardless of the format, will be recorded as the [strftime format](https://strftime.net) `%Y-%m-%d %H:%M:%S`. For example:
  
  - “May 31st, 2023 4:17:33pm” will be recorded as `2023-05-31 16:17:33`.
  - “May 31st, 2023” will be recorded as `2023-05-31 00:00:00`.
5. If a record field is defined as "text (fixed values)" but the incoming data is not one of the fixed text values defined for the field, empty data will be recorded and you'll see an error in the logs.

## Record artifacts

Record artifacts function similarly to regular fields in the records table, making them visible both in the user interface when accessing the records table and via the API as fields on individual record rows. However, there are two notable differences. 

Unlike typical fields, artifacts enable the storage of raw logs or extensive data dumps directly within the Records table. Each artifact can accommodate up to 15,000 characters of data. 

Querying Record artifacts is not supported. To fully leverage this expanded storage capability, it's advisable to perform preprocessing at storage time.

> **TIP:**
> Record artifacts can't be queried directly; only structured record fields can. When you store raw data in an artifact, also parse the values you'll later want to filter, group, or chart by, and capture them as dedicated fields on the same record.
> 
> For example, when storing a full alert log in a record artifact, capture fields such as source, severity, and threat type alongside it. Those fields stay attached to the same record as the artifact, so you can query and visualize them without scanning the full payload.

## Navigate the records table

The records table is a view of all created records by record type. You can navigate to the records table from the storyboard or in the main top navigation by clicking `**records**`.

The fields on the record table will match the record type settings, and can be rearranged to best fit your needs.

Records charts allow you to visualize your data in multiple formats including: line, bar, pie, or stacked area charts.

**

### Add charts for records reports

Add multiple data visualizations to a report for a more in-depth view. Customize them to fit your needs with the options to:

1. Rearrange and resize charts within the grid
2. Display a count, sum, or average for a record
3. Asynchronous loading so you can manually refresh the charts for improved performance

### Filter the records table

There are a few ways to filter the records view. You can filter by one or a combination of the options. 

1. Stories: select the story(ies) for which you want to see the associated records
2. Record table fields: filter by a specific field or the data within the field (note that the available operators are different for different field types)
  
  1. Equals (case insensitive)
  2. Does not equal (case insensitive)
  3. Is any of (case insensitive)
  4. Is none of (case insensitive)
  5. Is empty
  6. Is not empty
  7. Greater than or equal
  8. Greater than
  9. Less than or equal
  10. Less than
  11. Is true
  12. Is false
3. Date: set a timeframe for the records
4. Test or Live environment*: select to see records associated with the test or live version of a story(ies) 

**Change control must be enabled in order to select between test and live environments. *

The record table automatically updates based on the filters defined. 

### Export the records table

You can export a record view to a CSV by clicking the export button. It will export the data as you see it within the table.

## Parent-child relationships

Parent and child records allow you to create dependencies between records. The parent record denotes the source data capture for the parent-child relationship. 

When added to a case, all child records of the parent record are also added to the case.

> **NOTE:** For example: say we had an ***Alert*** record type in a story and wanted to include other downstream "observables" as part of that record type. We would set the downstream records up with the parent record, Alert.

### Configuring the parent record

In the build pane of the child record, you will turn on **`parent record`**.

Within the formula field, you will find the record the exact same way you pull event data. 

**

**Note**: your parent record must be linked upstream of the child record.

### Live and test records

Tines records are a great way to capture and store data for use within stories. In some situations, you may want to capture and interact with **live** records, which are used with other stories and dashboards, or **test** records, which can help ensure your story operates as expected.

## Viewing test records

Each record type supports a **live** or **test** record classification. This classification ensures that you can: 

1. Avoid contaminating your live records and dashboards when testing your story
2. Populate a draft story with live record data

> **INFO:** Classifying a record as **live** or **test** permanently segregates that record in the record table and assigns an immutable Record ID. Once a record is created, is distinct and cannot be moved between classifications.

To view your live or test records, use the toggle within the Records table.

**

## "Live mode" toggle

The record tile on your story board has five operational modes: `Create`, `Update`, `Delete`, `List`, and `Get`.

The `List` and `Get` modes can be configured to explicitly interact with **live** or **test** records via the "**Live mode**" toggle.

**

## Change control

When using [change control](https://www.tines.com/docs/change-control/), the record tile modifies either the **live** or **test** record tables depending on whether the story is a draft or live.

### Draft story mode

When working within a "draft" story, the record tile modifies the **test** record table by default.

#### **Inherent record classification**

- `Create`: This mode creates a **test** record only.
- `Update`:** **This mode updates a specific **test** record based on the `Record ID` field. If the record ID does not exist in the **test** record table, the update action fails.
- `Delete`:** **This mode deletes a specific **test** record based on the `Record ID` field if. If the record ID does not exist in the **test** record table, the delete action fails.

#### Explicit record classification

- `List`: The "live mode" toggle automatically defaults to `off`, then lists all **test** records that meet the filter criteria.
- `Get`: The "live mode" toggle automatically default to `off`, then returns a specific **test** record based on the `Record ID` field.

**

### **Live story mode**

When the draft story is published, the `Create`, `Update`, `Delete`, `List`, and `Get` record modes will interact with **live** records by default.

#### **Inherent record classification**

- `Create`:** **This mode creates a **live** record only.
- `Update`:** **This mode updates a specific **live** record based on the `Record ID` field. If the record ID does not exist in the **live** record table, the update action will fail.
- `Delete`:** **This mode deletes a specific **live** record based on the `Record ID` field if. If the record ID does not exist in the **live** record table, the delete action will fail.

#### Explicit record classification

- `List`: The "live mode" toggle automatically defaults to `on`, then lists all **live** records that meet the filter criteria.
- `Get`: The "live mode" toggle automatically defaults to `on`, then returns a specific **live** record based on the `Record ID` field.

## "Live mode" override

When using the `Get` and `List` record modes, the "live mode" toggle setting can be overriden.

This allows you to request **live** records while in a draft story, or **test** records when change control is disabled (or if unavailable).

> **WARNING:**
> If you manually toggle the "live mode" on or off, the selection will permanently be configured. This means the "live mode" toggle no longer automatically update to:
> 
> -   `off` when creating or updating a draft story
> -   `on` when publishing a draft story to live

**

### Record usage and warnings

Tines monitors record usage across your tenant and warns you when you're approaching a limit. This page explains the three limits that apply, the warnings you'll see, and how to resolve them.

## Limits overview

When using records, there are three primary limits to consider:

| Limit | Description | Configuration |
| --- | --- | --- |
| **Records per record type** | Maximum number of records a single record type can hold | 
This limit is configurable within each record type using [retention and eviction](https://www.tines.com/docs/records/record-retention/) policies.

Otherwise it is determined by the [tenant limit](https://www.tines.com/docs/records/limits/).

 |
| **Total record types** | Maximum number of record types you can create in your tenant. | Determined by the [tenant limit](https://www.tines.com/docs/records/limits/). |
| **Total records** | Maximum number of records across all record types combined. | Determined by the [tenant limit](https://www.tines.com/docs/records/limits/). |

## Usage warnings

Tines displays warnings when you approach or reach these limits.

### Records per record type

Each record type has a maximum number of records it can hold. This is determined by your tenant's plan and can be further reduced by setting a custom record limit on the record type.

| Scenario | Condition | 
Description

 | 

Notification types

 |
| --- | --- | --- | --- |
| **Approaching limit** | Warning at 80% usage | 

A warning appears when the number of records in a record type reaches the default or configured maximum limit.

 | 

Gold warning banner is displayed:

-   Affected record type row in the list of record types
-   Viewing the affected record type

Tenant admin will receive an email.

 |
| **Limit reached** | Notifcation at 100% usage | 

A notification appears when the number of records in a record type reaches or exceeds the default or configured maximum limit.

 | 

Red notification banner is displayed:

-   Affected record type row in the list of record types
-   Viewing the affected record type

Tenant admin will receive an email.

 |

#### Notifications in the record type list

The record type list indicates when a record limit is approaching or has been reached.

![](https://www.datocms-assets.com/55802/1778696571-warning.png)

![](https://www.datocms-assets.com/55802/1778696606-records-list-limit-reached.png)

#### Notifications on the record type page

![](https://www.datocms-assets.com/55802/1778696728-record-type-page-warning.png)

![](https://www.datocms-assets.com/55802/1778700436-record-type-page-limit-reached.png)

#### Email notification

This is an example of the email notification sent to team and tenant admins. Tines sends notifications to:

**Team admins**

- Warning when nearing 80% of records limit in a record type
- Notification when reaching the records limit in a record type

**Tenant admins**

- Warning when nearing 70% of the total records limit for the tenant
- Notification when reaching the total records limit for the tenant

![](https://www.datocms-assets.com/55802/1778696890-email-warning.png)



### Total record types

Each tenant has a maximum number of record types you can create. This maximum applies collectively across teams.

| Scenario | Condition | 
Description

 | 

Notification type

 |
| --- | --- | --- | --- |
| **Limit reached** | Notifcation at 100% usage | 

Creating a new record type will fail and display a notification.

 | 

A notification toaster displays indicating the limit has been reached.

> Maximum number of record types for this team has been reached

 |



### Total records

Each tenant has a maximum number of records it can store across all record types and teams.

| Scenario | Condition | 
Description

 | 

Notification types

 |
| --- | --- | --- | --- |
| **Approaching limit** | Warning at 70% usage | 

A warning appears on the record type list for all teams when the total number of records in a tenant reaches 70% of the tenant limit.

 | 

Gold warning banner is displayed on all team record type pages.

Tenant admin will receive an email.

 |
| **Limit reached** | Notifcation at 100% usage | 

A notification appears on the record type list for all teams when the total number of records in a tenant reaches the tenant limit.

 | 

Red notification banner is displayed on all team record type pages.

Tenant admin will receive an email.

 |

#### Notifications in the record type list

![](https://www.datocms-assets.com/55802/1778698567-record-type-total-warning.png)

![](https://www.datocms-assets.com/55802/1778698574-record-type-total-notification.png)

## What happens when a limit is reached

When you reach a record limit, the following occurs.

| Limit | 
Outcome

 |
| --- | --- |
| **Records per record type** | 

Depending on the retention and eviction policy setting within a record type:

-   "**Stop creating records**": No new records will be created
-   "**Evict oldest records**": A record will be deleted based on the age settings and allow a new record to be created.

 |
| **Total record types** | No additional record types can be created |
| **Total records** | No additional records can be created. Stories will error when attempting to capture a new record. |

## How to resolve warnings

The actions available depend on which limit you've reached:

#### Record limit reached

- **Delete records** you no longer need, either individually or in bulk from the record type.
- **Set a retention policy** on the record type to automatically delete records older than a specified duration (from one day to 10 years).
- **Enable "evict oldest records" mode** on the record type. Tines automatically removes the oldest records to make room for new ones, and replaces the warning banners with a neutral informational message.
- **Increase the custom record limit** if one has been set on the record type.

#### Record type limit reached

- **Delete record types** you no longer need.
- **Consolidate record types** that store similar data to reduce your total count.

### Dismissing banners

You can dismiss in-app warning banners. Dismissed banners stay hidden for seven days before reappearing if the condition still applies.

### Email frequency

Tines sends limit notification emails at most once per day per admin for each warning level. Per-record-type emails are checked each time a record is created, while per-tenant emails are checked once daily.

## Best practices

- **Set retention policies proactively.** Don't wait for a warning – configure retention on high-volume record types from the start.
- **Use evict-oldest mode for ephemeral data.** If a record type holds transient data (like enrichment cache or log snapshots), eviction keeps it self-managing.
- **Monitor usage with dashboards.** Build a dashboard that tracks record counts per type so you can spot growth trends early.
- **Review your record type count periodically.** Consolidate record types that store similar data to stay within your tenant's limit.

## Related pages

- [Records limits](https://www.tines.com/docs/limits/)
- [Record retention and eviction](https://www.tines.com/docs/record-retention/)

### Record retention and eviction

Control how long records are kept (retention) and what happens when a record type reaches capacity (eviction). These settings help you manage record storage, stay within your tenant's limits, and keep record data current.

## When to use retention and eviction

Configure these settings when you want to:

- **Automatically clean up stale data** — for example, delete alert records older than 90 days.
- **Cap a high-volume record type** — prevent one record type from consuming your entire tenant allowance.
- **Keep a rolling window of data** — evict the oldest records as new ones arrive, useful for metrics or ephemeral state.

## Configure the record type

When editing a record type, you are presented with options to define retention and eviction settings. 

Retention and eviction are not enabled by default – When the [limit](https://www.tines.com/docs/records/limits) is reached an error is shown and new records are not created.

### Custom record limit

Each record type has a maximum set by your tenant limit. To cap a specific record type below that maximum, turn on **Custom record limit** and enter a value (minimum of one).

Test and live records count toward the same limit.

![](https://www.datocms-assets.com/55802/1777498838-records-record-limit.png)

### Retention period

Set a retention period to automatically delete records after the configured amount of time. Choose from retention durations between one day and 10 years.

> **INFO:** Records without a configured retention period are kept for the duration of your active Tines license.

![](https://www.datocms-assets.com/55802/1777498864-records-retention-period.png)

### Record age is based on

This setting controls how the age of a record is evaluated for both time-based retention or eviction when the record limit is reached. 

- **Created at** — the time the record was first created.
- **Updated at** — the most recent time any field on the record changed.

> **TIP:** This option is shown when **Retention period** is enabled *or* **When record limit is reached** is set to "Evict oldest records".

![](https://www.datocms-assets.com/55802/1777498895-records-record-age-is-based-on.png)

### When record limit is reached

This setting controls what happens when a record type hits its limit:

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 53.5581%;"><col style="width: 23.2209%;"><col style="width: 23.2209%;"></colgroup><tbody><tr><th class="TableHeader_t1c5hy0"><span>Option</span></th><th><span>Available on</span></th><th class="TableHeader_t1c5hy0"><span>ourcome</span></th></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Send warnings and continue creating records</strong></span></td><td><p><span>• Self-hosted tenants</span></p></td><td class="TableCell_t8mlxr3"><span>Tenant admins are emailed when the record type reaches 80% and 100% of its limit. New records creation is <strong>allowed</strong>.</span></td></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Send warnings and stop creating records</strong></span></td><td><p><span>• Cloud tenants<br></span></p><p><span>• Self-hosted tenants</span></p></td><td class="TableCell_t8mlxr3"><span>Tenant admins are emailed when the record type reaches 80% its limit. New record creation is <strong>blocked</strong>.</span></td></tr><tr><td class="TableCell_t8mlxr3"><span><strong>Evict oldest records</strong></span></td><td><p><span>• Cloud tenants<br></span></p><p><span>• Self-hosted tenants</span></p></td><td class="TableCell_t8mlxr3"><span>Tines automatically deletes the oldest records as determined by the<span>&nbsp;</span><strong>Record age is based on</strong> setting.</span></td></tr></tbody></table>

![](https://www.datocms-assets.com/55802/1777498912-records-when-limit-is-reached.png)

## Additional considerations

1. Test and live records are collectively considered against the record type's limit 
2. Test and live records are both retained and evicted together
3. Deleted or evicted records cannot be recovered
4. When a parent record is deleted, child records remain but their link to the parent is removed.

## Best practices

- **Start with "Send warnings" before enabling eviction.** This gives you visibility into how quickly a record type fills up before you commit to automatic deletion.
- **Use "Updated at" for living data.** If records represent ongoing state (inventory, configurations), basing age on the last update keeps active records from being evicted.
- **Set custom limits or shorter retention on high-volume record types.** This prevents a single noisy record type from consuming your tenant's entire record allowance.
- **Pair retention periods with eviction thoughtfully.** If both are configured, whichever threshold is hit first triggers deletion.

### Records limits

The limitations of records, record types, and record fields are as follows.

## Record limits

These are the default limits when using records. Check your plan type in **Settings** > **Billing** then refer to the table below. 

- **Records per record type**: The maximum number of records for an individual record type.
- **Total record types**: The maximum number of record types you can create across all teams in your tenant.
- **Total records**: The maximum number of records aggregated across all record types.

> **TIP:** Configure [record retention and eviction](/docs/records/record-retention/) to precisely manage record usage and allowances within each record type.

|  | **Starter** | **Essentials** | **Standard** | **Advanced** | **Enterprise Lite** | **Enterprise L1** | **Enterprise L2** | **Enterprise L3** | **All Self-hosted** |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Record types per tenant** | 5 | 50 | 100 | 150 | 150 | 250 | 500 | 1,000 | 1,000 |
| **Records per Record Type** | 1,000 | 1M | 1M | 1M | 1M | 2M | 2M | 2M | 2M |
| **Max total records** | 5,000 | 5M | 5M | 10M | 10M | 20M | 30M | 40M | No limit |



## Record types

<table border="1" style="border-collapse: collapse;"><tbody><tr><td><strong>Limit</strong></td><td><strong>Description</strong></td></tr><tr><td>Record retention</td><td><p>Configurable within each record type for up to 10 years.</p><p>Records without a configured retention are kept for the duration of the customer’s active license.</p></td></tr></tbody></table>

## Record charts

<table border="1" style="border-collapse: collapse;"><tbody><tr><td><strong>Limit</strong></td><td><strong>Description</strong></td></tr><tr><td>Charts per record type</td><td>10 charts</td></tr><tr><td>CSV export size</td><td>100k rows</td></tr></tbody></table>

## Record fields

<table border="1" style="border-collapse: collapse;"><tbody><tr><td><strong>Limit</strong></td><td><strong>Description</strong></td></tr><tr><td>Field definitions per Record Type</td><td>50</td></tr><tr><td>Validation options per field</td><td>100</td></tr><tr><td>Size of "Numeric" field type</td><td>Up to 7 places</td></tr><tr><td>Size of "Text" field type</td><td>512 characters</td></tr><tr><td>Size of "Large text" (artifact) field type</td><td>15k characters</td></tr><tr><td>Validation options for fixed text fields</td><td>Up to 100</td></tr><tr><td>Max size of a record row</td><td>1MB</td></tr></tbody></table>

## Record API Limits

The limitations when calling the [records API](https://www.tines.com/api/records/) are as follows.

<table border="1" height="342" style="border-collapse: collapse;"><tbody><tr><td><strong>Limit</strong></td><td><strong>Description</strong></td></tr><tr><td>Requests per minute</td><td>400</td></tr><tr><td>Page size per request</td><td>500</td></tr></tbody></table>

## Additional limits

Dashboard charts have similar limits to records, for more information please see [Dashboard limits](https://www.tines.com/docs/limits/).

For more information on the limitations around cases, please see [Case limits](https://www.tines.com/docs/limits/).

## Dashboards

# Introduction 

Dashboards offer a place to visualize data from records or cases in a single view. These views help you monitor what's happening across workflows within your team. 

**

## Components

There are three types of components on the dashboard: 

1. **Charts**: created from your records data to visualize records with applied filters in multiple chart types for an aggregate view
2. **Case Views**: filtered view of cases or case summary data (i.e. MTTR, MTTA)
3. **Notes**: context to provide alongside the charts or lists with Markdown support 

# Configuring dashboards

## Create a dashboard

1. Click New Dashboard
2. Name the dashboard (this must be unique and can be edited at any time)

## Add a note to the dashboard

Notes let you add explanations and context to your dashboard

![](https://www.datocms-assets.com/55802/1718800868-screenshot-2024-06-19-at-1-40-39-pm.png)

*Dashboard Note*

## Add a Case view to the dashboard

Case views allow you to create some basic charts based on a saved case view or a new search. To create a case view, follow these steps.

1. Add a new Case view element to the dashboard
2. Name the element
3. Choose to use a saved cases view or use the search filters to query for the data you want
4. Select the display option for how you want to visualise the results

**

### MTTR & MTTA Case views

Case views include the option to display and graph MTTR (mean time to resolve) & MTTA (mean time to assign) values for cases within a team. 

The time to resolve value for a case is calculated by the difference between the timestamps of when a case was opened (`created_at`) and when it is closed (`resolved_at`). Similarly, the time to assign value for a case is calculated by the difference between the time the case was opened (`created_at`) and a user first being assigned to the case (`created_at` on the first user assigned activity record).

##  Add a Records chart visualization to the dashboard

You can also add Records graphs into your dashboard to add visualizations of data captured via Records

**

> **NOTE:** Each dashboard can have up to 15 elements.

### Dashboard limits

## Dashboards

The limitations when creating and configuring dashboards are as follows.

<table border="1" height="226" style="border-collapse: collapse;"><tbody><tr><td>Type</td><td>Limit</td></tr><tr><td>Number of dashboards per team or group</td><td>20</td></tr><tr><td>Number of charts per dashboard</td><td>30</td></tr><tr><td>Characters allowed in a dashboard title</td><td>100</td></tr><tr><td>Number of scheduled snapshots per dashboard</td><td>10</td></tr><tr><td>Recipients per snapshot</td><td>50</td></tr></tbody></table>

## Additional limits

Dashboard charts have similar limits to record charts, for more information please see [Records limits](https://www.tines.com/docs/limits/).

For more information on the limitations around cases, please see [Case limits](https://www.tines.com/docs/limits/).

## Workbench

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

## Introduction

Workbench is a familiar, Tines-powered AI chat interface to take action and access proprietary data in real-time, privately and securely. It has the general knowledge capabilities of the leading LLM it’s powered by, and it can run various tools configured in Tines. In keeping with all Tines AI features, your data is private and secure, never leaving your tenant.

**

## Conversation

Type into the chat box to start a conversation with Workbench. Conversations are individual to the user. Within each conversation, Workbench will have the context of all messages sent. Users can get real-time data from their systems and take actions by allowing Workbench to use tools. 

### Tools

When a tool is run, a user can view the JSON input and output of the tool within the conversation.  There are three types of tools supported:

1. **Templates**
  
  Templates are singular actions provided by Tines. To enable a template for access, users must:
  
  1. Select or create a [credential](https://www.tines.com/docs/credentials/) that corresponds with the template's product
  2. Select the specific templates that Workbench can access
  
  Templates can be enabled or disabled through the green or grey dot. Templates that may make changes to your environment will prompt the user to confirm before running.
2. **Stories**
  
  Stories are custom workflows built by users. To create a story that Workbench can access, users must:
  
  1. Define a name for the story
  2. Define a description of what your story does
  3. Configure the following send to story settings:
    
    1. [Send to story](https://www.tines.com/docs/stories/send-to-story/#enabling-a-story-for-send-to-story-creating-a-sub-story) setting is toggled on
    2. [ Input](https://www.tines.com/docs/stories/send-to-story/#input) and [output](https://www.tines.com/docs/stories/send-to-story/#output) actions are configured
    3.  [Input parameters](https://www.tines.com/docs/stories/send-to-story/#send-to-story-inputs) are defined
    4. Toggle `Enable for:` to either `Workbench` or `Workbench and Send to Story` 
      
      Note: if `Enable for Workbench` is toggled on, the story can only be called via Workbench and cannot run autonomously (i.e. scheduled actions will not run and the webhook will not accept events from outside of Workbench). The story will not count towards any license counts.
  4. Toggle `Require confirmation to run` is set to your preference. If turned on, Workbench will not run the story without user confirmation.
  5. Within Workbench, stories can be enabled or disabled by selecting the green or grey enabled dot. [Learn more about how to use stories with Workbench here](https://explained.tines.com/en/articles/9855926-using-stories-with-workbench). 
3. **Internal tools**
  
  Workbench has built-in functionalities designed to enhance and extend the capabilities of Workbench without additional configuration. All internal tools work with custom AI providers. 
  
  - **Think**
    
    A scratchpad that Workbench can use to plan out its actions before taking them. Designed based on [Anthropic's research](https://anthropic.com/engineering/claude-think-tool).
  - **Code analyst**
    
    Generates and runs Python code. Generated Python code can create and render images inline allowing for complex data transformations and visualizations. Works on cloud with no additional configuration. Self-hosted instances will have to enable run script.
  - **Formulas docs**
    
    Provides you with the latest up to date documentation on Tines formulas allowing you to ask questions around how to use specific formulas or how to achieve a specific goal using formulas.  

![](https://www.datocms-assets.com/55802/1726087740-config.png)

#### Tool file outputs

Any tool type can return a file to a user.

![](https://www.datocms-assets.com/55802/1753361634-file-response.png)

The tool must format the response in the following format:

```json
{
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "image",
        "document"
      ],
      "description": "The type of content being returned"
    },
    "filename": {
      "type": "string",
      "description": "Name of the file (optional)"
    },
    "contents": {
      "type": "string",
      "description": "Base64 encoded content"
    }
  },
  "required": [
    "type",
    "contents"
  ]
}
```

### File attachments

Workbench allows users to attach the following file types:

- Images: `PNG`, `JPEG`, `GIF`, `WEBP`
- Documents: `PDF`, `CSV`, `MD`, `TXT` , `DOC`, `DOCX`, `XLS`, `XLSX`, `YAML`

Attachment limits:

- Users can attach up to three files per message.
- Each attachment may be up to 4.5MB in size.

If you're using a provider other than the default Tines provider, supported file types may vary.

### Chat history

View chat history by selecting the arrow in the top middle of Workbench. Users can search for existing chats, rename, and delete them. Select a chat to load the contents in Workbench and continue the conversation. Chat history retention and the ability to delete chats varies by Workbench plan, [learn more here]( https://explained.tines.com/en/articles/9855931-how-to-access-workbench). 

### Custom instructions

Configure custom instructions via the "Custom instructions" settings in the bottom left of Workbench. Custom instructions can define context for Workbench and will be processed by Workbench with each message sent. They are immediately applied to all existing conversations.



[](https://demo.arcade.software/T1nYpBKhTmsk7J2XbvFX?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

## Access

Workbench is scoped to the individual user. Tool configurations, chats, and custom instructions are individual to each user. Within Workbench, users can enable credentials or stories they have access to run within Tines. Specifically,

- Credentials: 
  
  - A user can access credentials that live in or are shared with their drafts in Workbench
  - A user with permissions `story: run`, `story: update` , and `story: view`, (editor and team admin roles have these permissions) in a team can access any credentials that live in or are shared with that team
- Stories:
  
  - A user can access Workbench configured stories that live in or are shared with their drafts 
  - A user with permissions `story: run`, `story: update` , and `story: view`, (editor and team admin roles have these permissions) in a team can access any Workbench configured stories that live in or are shared with that team

> **INFO:** All actions performed by a user in Workbench are accessible via audit logs.

## Send a case or event to Workbench

[**Case**](https://www.tines.com/docs/cases/)**:**

To send a case to Workbench, select the kebab menu on the top right of the case and select `Load in Workbench`. This will open a new chat with Workbench with the embedded context of your case. Updates to the case will be sync'd with the chat in real time.

![](https://www.datocms-assets.com/55802/1726087991-cases.png)

[](https://demo.arcade.software/77C8S4pRcwCwwpViX3Fg?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)



[**Event**](https://www.tines.com/docs/events/): 

To send an event to Workbench, use the [function](https://www.tines.com/docs/formulas/functions/workbench-link/) `WORKBENCH_LINK()`. This will produce a link to Workbench. When a user opens this link, it will open a new chat with Workbench with the embedded context of your event. The latest details of this event will be available to Workbench in real-time.

## Convert Workbench chat to story

Convert your Workbench chat to a story by navigating to chat history, locating the chat to convert, and selecting the lightning icon. This will generate a story with the actions and stories run in your Workbench chat that can be used as a rich starting point to turn into a real and reusable workflow.

## Mobile support

Workbench supports a mobile chat experience for iPhone and Android devices. To access Workbench, visit your tenant URL in your mobile browser. Select `Add to your Home Screen` to create a more native app like experience. For optimal experience, certain features are not supported via mobile such as tool configuration and viewing tool inputs and outputs.

## Keyboard shortcuts

- `N` starts a new conversation when viewing an existing one
- `/` focuses the input cursor on the chat bar
- `Esc` removes the input cursor from the chat bar
- `⌘[` toggles the sidebar open and close

## Presets

Presets allow team admins (or custom roles with the permission to manage the team) to pre-configure a set of templates (with credentials), stories, and custom instructions; making available to all team members for rapid and consistent use.

[](https://demo.arcade.software/8VLUhgkKrzu1x8njPVoU?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

#### Creating a preset

In the default ‘All tools’ view of Workbench, configure the templates, credentials, and stories you want to make available. Ensure you only choose credentials and stories that the team has access to. Then, hit ‘Create preset’, choosing the correct team and assigning a name and optional custom instructions.

#### Editing a preset

To edit an existing preset, select the preset from the dropdown menu, then click the 'Edit preset' button. This allows you to modify the templates, credentials, and stories associated with the preset. All team members will immediately have access to the updated configuration.

**

To rename the preset or edit the instructions, click the settings icon next to the 'Edit preset' button.

#### Deleting a preset

To delete a preset, click the settings icon and then click the 'Delete preset' button at the bottom.

## Pre-populate chat input via query parameter

You can include an `initialMessage` query parameter in the Workbench URL to pre-populate the chat input with a custom message. This makes it easy to set up a Chrome Site Search shortcut (custom search engine) for launching Workbench with a ready-to-use prompt.

#### How it works

Construct a Workbench URL like: `https://[your-subdomain].tines.com/workbench/?initialMessage=Tell+me+a+joke`

#### Set up a Chrome Site Search shortcut

1. In Chrome, go to **Settings → Search engine → Manage search engines and site search**.
2. Click **Add**, and fill in:
  
  - **Search engine**: e.g. `Ask Workbench`
  - **Shortcut**: e.g. `@wb`
  - **URL with **`**%s**`** in place of query**: `https://[your-subdomain].tines.com/workbench/?initialMessage=%s`
3. Now in Chrome’s URL box, type `@wb` (or your chosen shortcut), press **Tab**, then enter your prompt—Work bench chat launches with that message pre‑filled.

### Workbench in Slack

## Introduction

Workbench in Slack brings the power of AI in Tines directly into your team’s Slack workspace, allowing you to take action and access proprietary data in real-time—all within Slack. With Workbench's secure, private AI capabilities, you can run tools configured in Tines, automate workflows, and get intelligent responses without switching contexts. Conversations started via Slack are also accessible within your Tines tenant.

## Installation

### Quick Start for Cloud Tenants

1. Navigate to `/workbench`.
2. Click the Slack icon button as shown in the screenshot below:

![](https://www.datocms-assets.com/55802/1738670117-sw-1.png)

  3. Follow the steps in the installation modal:

- Note: Only **tenant owners** can install the Tines Slack Workbench app.
- A success message like the one below indicates the bot is installed:

![](https://www.datocms-assets.com/55802/1738670191-sw-2.png)

  4. Once installed, go to the Slack Workspace where you installed the bot and search for `Workbench`.

  5. Start chatting with Workbench in Slack 🚀

### Using the Tines Workbench Slack App

- Recommended for cloud tenants.
- Events are routed through our centralized US-based router before reaching your Tines tenant.
- If your Slack Workspace has [app approval](https://slack.com/intl/en-ie/help/articles/222386767-Manage-app-approval-for-your-workspace#h_01EC8H3AWBYEAAN5AKBTVKPC5K) enabled:
  
  1. Request approval from your Slack Workspace admins.
  2. Complete the installation after approval is granted.

### Using a Custom Slack App

This option is ideal if:

- You’re using a self-hosted tenant.
- You manage multiple cloud tenants within a single Slack Workspace (each requires a custom Slack app).
- You want to avoid routing Slack events via the Tines US-based router.

---

## Usage

### Starting a New Conversation

- Search for "Workbench" in Slack, select the Workbench app, and click “New chat.”
- Tip: Pin Workbench to the top bar of Slack for easy access.
  
  - Slack admins can enable this at the Workspace level for all users ([see](https://slack.com/intl/en-ie/help/articles/33077521383059-Display-AI-apps-in-Slack#pro-and-business+-subscriptions-1)).

![](https://www.datocms-assets.com/55802/1739191780-screenshot-2025-02-10-at-12-49-29.png)

### Preset Selection

- When starting a new conversation, you can choose a preset.

![](https://www.datocms-assets.com/55802/1739191835-screenshot-2025-02-10-at-12-50-23.png)

- Available presets are tied to your Tines user, with a default preset applied if none is selected. Note that the default preset is the `All tools` preset.
- Selecting a preset applies it to the current conversation and provides a link to view the conversation in Tines.

![](https://www.datocms-assets.com/55802/1739191892-screenshot-2025-02-10-at-12-51-23.png)

### Tool Usage

### Tool Confirmations

For tools requiring confirmation, users will see:

- The tool name.
- Input details.
- Confirm and cancel buttons.

![](https://www.datocms-assets.com/55802/1739192057-screenshot-2025-02-10-at-12-54-08.png)

### Workbench limits

<table border="1" height="263" style="border-collapse: collapse;"><tbody><tr><td>Element</td><td>Description</td></tr><tr><td>Context window</td><td><p>Model dependent<br><em>200,000 tokens<br></em><em>1 million tokens</em></p></td></tr><tr><td>Max output</td><td>8,192 tokens</td></tr><tr><td>Messages &amp; tool runs per minute per tenant</td><td><p>25 (EU tenants)</p><p>50 (US tenants)</p><p>100 (custom AI provider)</p></td></tr><tr><td>Input tokens per minute per tenant</td><td><p>300,000 (EU tenants)</p><p>500,000 (US tenants)</p><p>1,000,000 (custom AI provider)</p></td></tr><tr><td>Default truncation limit (can be disabled)</td><td><p>50,000 tokens</p></td></tr></tbody></table>

To better understand what a token is, check out [Anthropic's docs here](https://docs.anthropic.com/en/docs/resources/glossary#tokens).

## Admin

### AI

AI in Tines is available for all tenants, and can be configured by a tenant owner. 
[Learn more about AI in Tines.](https://explained.tines.com/en/articles/12801322-what-is-ai-in-tines)

## Enable AI features

Control whether or not AI features are enabled across the whole tenant. These can be turned on via the AI settings in the settings center. By default, AI is turned on for newly created tenants.

For more granular control, expand **Advanced options** to enable or disable individual AI features. You can also choose whether newly released AI features are enabled by default.

[](https://demo.arcade.software/Ja9xMqPpAsgHWFVjIDQR?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

## AI providers

By default, AI features are powered by Anthropic's Claude, hosted securely through AWS Bedrock. 

It is possible to bring your own AI provider to power all of our AI features. The following AI providers are configurable with the following customizations:

<table border="1" height="269" style="border-collapse: collapse;"><tbody><tr><td><strong>Provider</strong></td><td><strong>Base URL</strong></td><td><strong>API key</strong></td><td><strong>Custom models</strong></td><td><strong>Custom headers</strong></td></tr><tr><td><a href="https://openai.com/api" target="_blank" rel="noopener">OpenAI</a></td><td><span role="img" aria-label="✅">✅</span></td><td><span role="img" aria-label="✅">✅</span></td><td><span role="img" aria-label="✅">✅</span></td><td><span role="img" aria-label="✅">✅</span></td></tr><tr><td><a href="https://www.anthropic.com/api" target="_blank" rel="noopener">Anthropic</a></td><td><span role="img" aria-label="✅">✅</span><br></td><td><span role="img" aria-label="✅">✅</span></td><td><span role="img" aria-label="✅">✅</span></td><td><span role="img" aria-label="✅">✅</span></td></tr><tr><td><a href="https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html" target="_blank" rel="noopener">AWS Bedrock</a> (cloud + <a href="https://www.tines.com/docs/self-hosting/additional-configuration/ai/">self-hosted</a><a href="https://www.tines.com/docs/self-hosting/additional-configuration/ai/"></a>)</td><td><span role="img" aria-label="✅">✅</span></td><td><span role="img" aria-label="✅">✅</span><br></td><td><span role="img" aria-label="✅">✅</span></td><td><span role="img" aria-label="✅">✅</span><br></td></tr></tbody></table>

AI providers, observability tools, or custom proxies which are schema compatible with the following providers can also be configured as well. This includes but is not limited to:

- OpenAI / Anthropic behind a proxy or service
- [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
- [Helicone](https://helicone.ai/)
- [OpenRouter](https://openrouter.ai)
- [xAI](https://x.ai)
- [Ollama](https://ollama.com/blog/openai-compatibility)

### Credentials and formulas in provider configuration

When configuring an OpenAI or Anthropic compatible AI provider, you can use credentials and formulas in the API key and header fields instead of pasting raw secrets.

To use credentials, associate the provider with a team. Once associated, any credential owned by that team is available to reference in the provider configuration. The provider itself remains available for use tenant-wide.

**

### Multiple AI provider support

You can enable multiple AI providers within a single tenant.
Once enabled, Tines lets you choose models from any of your active providers when configuring:

- AI Agent actions on the storyboard
- The tenant's default smart and fast models in AI settings
- The selected model for a Workbench conversation

This gives you the flexibility to mix and match providers and models while keeping control over exactly which models are exposed in your tenant.


**Note:** Tines AI features require at least one active AI provider. If a provider is being used as the default smart or fast model, it must remain enabled until you switch those defaults to another provider's models.

### Team scoping

AI providers can be scoped to specific teams, giving tenant owners more granular control over who can access each provider and its models. This is useful for:

- **Managing costs:** limit expensive models to teams that need them.
- **Enforcing compliance :** restrict providers based on data handling requirements.
- **Staged rollouts: **introduce new models to specific teams before enabling them tenant-wide.

The team that owns each provider's credential always has access. By default, all other teams also have access unless the provider is explicitly scoped.

### Schema compatibility

When using a custom AI provider that's not listed above, the provider must be schema compatible with the [OpenAI chat completions API](https://platform.openai.com/docs/api-reference/chat/create). The endpoint must support streaming and tool use.

Additionally, if the provider supports the [models list endpoint](https://platform.openai.com/docs/api-reference/models/list), Tines is able to auto discover models. If not, you can still add custom models manually from the models tab.

# AI models

When configuring OpenAI compatible APIs, it is possible to select which models are usable within Tines. Once configured, the fast model will power features such as automatic transform. The smart model will be used to power Workbench and other similar features. 

All custom models enabled on an AI provider are also accessible on the AI action.

**

### **Custom AWS Bedrock support for cloud**

With custom AWS Bedrock support for cloud, you can choose any models that are enabled in your AWS region and permitted by your Bedrock account.

We recommend enabling the latest Anthropic Claude models for best performance and capabilities.

Model IDs can be obtained from the [AWS Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html).

#### **Credentials for AWS Bedrock**

To securely invoke AWS Bedrock APIs, Tines supports [AWS authentication](https://www.tines.com/docs/credentials/aws/) by connecting a credential to the provider or through assumed roles for the instance (self-hosted only).

##### **Required IAM permissions**

To use Bedrock within Tines, the IAM role must include the following permissions:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BedrockModelAccessPermissions",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel*",
        "bedrock:GetInferenceProfile",
        "bedrock:ListInferenceProfiles",
        "bedrock:ListFoundationModels"
      ],
      "Resource": "*"
    }
  ]
}
```

#### **AWS Bedrock endpoints**

When configuring your connection to AWS Bedrock, you’ll need to specify the correct Amazon Bedrock runtime API endpoint for your AWS region.
It will look something like: `bedrock-runtime.<region>.amazonaws.com`

[View the list of region-specific Bedrock runtime endpoints.](https://docs.aws.amazon.com/general/latest/gr/bedrock.html)

![](https://www.datocms-assets.com/55802/1754482487-screenshot-2025-08-06-at-13-14-01.png)

## Azure OpenAI

When deploying OpenAI models through the [Azure AI Foundry](https://azure.microsoft.com/en-us/products/ai-services/openai-service), additional configuration is required to enable use of those models in Tines. Each model is deployed to a unique URL and must be manually added to the model list.

You will also need set the API Type in Extra Options to Azure

## Custom certificate authorities

When creating a custom AI provider, you can select the custom certificate authority to use for the connection. This can be helpful when connecting to your own private AI service.

## Tunnel

You can also configure a custom AI provider to connect via a Tines Tunnel. This can be configured under **Extra options** when configuring your provider. Connecting via a Tunnel allows your cloud tenant to securely access AI services running in your internal networks. Only Tunnels which are accessible by all teams can be used.

For more information visit the Tunnel docs [here](https://www.tines.com/docs/admin/tunnel/).

![](https://www.datocms-assets.com/55802/1757071816-tunnel-ai.png)

## AI credit management

You can top up your AI credit balance from the **Billing** screen in Settings - you can learn more about that [here](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits). If you need to increase your monthly AI credits, contact your Tines account team.

**Team allocations for AI actions & Workbench**

In the **AI settings** screen you can open **AI credit allocations & limits** to allocate your [AI credits](https://explained.tines.com/en/articles/9369092-ai-action-credits-executions) to runtime AI usage (AI action runs and Workbench conversations) in your teams. Allocating credits to a team reserves those credits for use within the team, reducing the amount that can be spend in teams without allocations. Tenant owners will receive email notifications when a team is nearing its allocated amount.

Note:

- Workbench conversations** **without a preset use the tenant's unallocated credits as these conversations are team agnostic.
- Workbench conversations** **using a preset will use the credits allocated to the team associated with that preset.

[](https://demo.arcade.software/OVf1ByHkZP6R59llULgB?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

**Team spending limits for Story copilot**

**Story copilot usage always comes from unallocated credits - this ensures that usage does not consume credits allocated for runtime AI usage in your teams.**

Under** Story copilot limits** you can set spending limits for your teams, and a single spending limit which will be applied to each personal team.

![](https://www.datocms-assets.com/55802/1777641561-screenshot-2026-05-01-at-2-19-04-p-m.png)

## Prompt caching

Tines automatically handles prompt caching when using Tines AI, OpenAI, Anthropic, or custom AWS Bedrock providers. Prompt caching reduces costs and latency by reusing previously processed prompt content. You can learn more about prompt caching [here](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html).

Prompt caching is ephemeral - cached content automatically expires within minutes and is not stored persistently. Caching does not affect data residency and does not change the security or compliance posture of your AI provider. 


![](https://www.datocms-assets.com/55802/1769519717-screenshot-2026-01-27-at-1-15-11-p-m.png)

*Prompt caching controls the amount of tokens that fall under **Cached input**. These tokens are processed faster and for a lower price.*

### Authentication settings

## Sign-in activity

Tines retains 2 years of sign in activity for each user. When a user signs in, Tines will record metadata such as Timestamp, IP address and the User agent associated with the sign in. These events are made available for download in CSV format from the [Audit Logs](https://www.tines.com/docs/admin/audit-logs/) page by filtering for `Login` operations.

## User sessions

Users are automatically logged out of their session after a period of inactivity. By default, this is set to one day.

User sessions can be configured and managed via Authentication settings (`https://<tenant-domain>/settings/authentication`). 

From here, tenant owners can configure user sessions to be an '`idle session timeout`' , or disable this option for timeouts to be a fixed duration from the time of sign in. They can also set the session duration via the dropdown, which supports a range of timeframes.

By default, the session has an absolute timeout from the moment a user logs in. When '`Idle Session Timeout`' is enabled, session expiry will instead be based on an idle timeout (i.e. user inactivity).

From the settings page, tenant owners can also invalidate all sessions in the tenant. Tenant owners can also deactivate specific users or invalidate their sessions from the Users page.

### Email-based login flow

> **INFO:** This is only enabled when SSO is not set up.



1. The user visits the URL for their Tines application, e.g. `https://tines.example.com`.
2. The user's browser doesn't yet have a session cookie for the application. The session cookie, if present, would provide a pointer to session data stored in a Redis cluster that would include the authenticated user's ID. As that session information is missing, the browser is redirected to the SAML IdP for their Tines instance. The redirect includes the necessary SAML request data. Out of the box, a Tines instance provides its own IdP, at `https://tines.example.com/saml_idp`, in our example.
3. The default SAML IdP provided out of the box by Tines authenticates users via magic links sent to their email address. The user is prompted for and provides their email address, `jane@example.com`.
4. The SAML IdP writes a database record with an identifier and the correct SAML response to later provide back to the SP and sends an email to `jane@example.com` containing a link like: `https://tines.example.com/saml_idp/email_callback?identifier=...`
5. When the user loads that link from their email inbox, the SAML IdP retrieves the database record associated with the given identifier and redirects the user back to the application proper (which is a SAML SP) at `https://tines.example.com/users/saml/auth` with the correct SAML response.
6. The application now knows that the user is indeed `jane@example.com`. It next validates that `jane@example.com` has an active account on the Tines application by looking for a corresponding user record in the database using that email address. If a user record is found, an entry is created in the Redis cluster that includes the record's ID. A session cookie with the correct identifier for retrieving that entry from Redis is created. The user's browser is redirected back to the page that they initially requested, `https://tines.example.com`.
7. The user's browser loads the page `https://tines.example.com` , this time providing a correct session cookie, which in turn allows the backend to load the authenticated user's ID from Redis and the user record from the database. The user is now logged in.

### Single sign-on

We optionally support single sign-on (SSO) on all Tines tenants, paid and [Community Edition](https://www.tines.com/blog/announcing-the-tines-community-edition) alike.

This allows users on your Tines account to sign in with their existing account on your external identity provider.

## Enabling SSO

To turn SSO on for your tenant, go to the settings center and choose "Authentication" under "Access & security" and follow the instructions below after selecting an SSO option.

> **TIP:** Consider generating [recovery codes](/docs/admin/recovery-codes/) for your user before setting up SSO so that you can regain access to your account in case of configuration issues.

> **NOTE:** SSO only enables users to sign in, not sign up. Users must be invited to gain access or [automated user provisioning](/docs/admin/user-administration/user-provisioning/) must be enabled.

## Disabling SSO

> **NOTE:** You should notify your users before making this change. Any users who were previously using SSO will now need to log in via default methods (email links, Google SSO, or Microsoft SSO).

To disable SSO in the Authentication settings UI, set it to "default" - [tines.com](http://tines.com/).

> **INFO:** If you are encountering an issue with your SSO on your self-hosted deployment of Tines and cannot access your tenant, please contact [support](https://www.tines.com/contact#support) to raise a priority request.

## SAML configuration

Setting up SSO via SAML requires two pieces of information from your external [SAML](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language) identity provider (IdP):

1. A URL, which we’ll redirect users to when signing in.
2. Your IdP’s X.509 public certificate. You can also instead use a fingerprint, which is a digest of the certificate. This must be generated using SHA-256, such as with [samltool.com](https://www.samltool.com/fingerprint.php). Using the fingerprint instead is discouraged, as it prevents extra security features.

> **WARNING:**
> If you will rely on SAML groups (e.g. for JIT or SSO-group-based page access), the group names need to be present in the SAML assertion without any additional tags or nesting.
> 
> Here is an example of a valid attribute value for a membership to the **Administrators** group:
> 
> <saml2:AttributeValue  
>   xmlns:xs="http://www.w3.org/2001/XMLSchema"  
>   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
>   xsi:type="xs:string">  
> Administrators  
> </saml2:AttributeValue>
> 
> If this is not the case with your identity provider, a transformation may be required.

### Enhanced SAML Security

Tines also supports additional SAML security features, such as encryption and signing. Contact our support team if you'd like to enable these for your tenant.

## OIDC configuration

Setting up SSO via OpenID Connect (OIDC) requires six pieces of information from your external OpenID provider (OP):

1. Your OAuth client ID.
2. Your OAuth client secret.
3. The authorization endpoint URL - e.g. `https://accounts.google.com/o/oauth2/v2/auth` for Google or `https://<YOUR-DOMAIN>/oauth2/authorize` for AWS Cognito.
4. The token endpoint URL - e.g. `https://accounts.google.com/o/oauth2/v2/auth` for Google or `https://<YOUR-DOMAIN>/oauth2/token` for AWS Cognito.
5. The issuer or issuer identifier URL - e.g. `https://accounts.google.com` for Google or `https://cognito-idp.<YOUR-REGION>.amazonaws.com/<YOUR-USER-POOL-ID>` for AWS Cognito.
6. The JSON Web Key Set (JWKS) URL - e.g. `https://www.googleapis.com/oauth2/v3/certs` for Google or `https://cognito-idp.<YOUR-REGION>.amazonaws.com/<YOUR-USER-POOL-ID>/.well-known/jwks.json` for AWS Cognito.

Items 3 to 6 can be found in your OP’s "provider metadata" or "discovery document", which is normally located at a URL that ends with `.well-known/openid-configuration` - e.g. `https://accounts.google.com/.well-known/openid-configuration` for Google or `https://cognito-idp.<YOUR-REGION>.amazonaws.com/<YOUR-USER-POOL-ID>/.well-known/openid-configuration` for AWS Cognito.

Tines requires that your OP supports the "Authorization code grant" OAuth flow and the "openid" and "profile" OAuth scopes.

### **Optional: email claim name**

By default, Tines reads the user's email from the `email` claim in the ID token, which works for Google, Okta, and most popular identity providers. Override this only if your provider returns the email address under a different claim (e.g., `mail`).

If you override this, make sure your identity provider is configured to include the chosen claim in the ID token. If the claim is missing from the ID token, sign-in will fail.

## Just-in-time user provisioning

See [here](https://www.tines.com/docs/admin/user-administration/jit) for documentation on just-in-time configuration.

## SCIM user provisioning

See [here](https://www.tines.com/docs/admin/user-administration/scim) for documentation on SCIM integration.

## SSO-group-based page access

> **NOTE:** This feature is not available in all plans, please reach out to your Tines point of contact or [Tines support](mailto:support@tines.com) to learn more about enabling it.

It's also possible to restrict access to specific pages based on SSO group membership. To enable this feature, you need to turn on **SSO-group-based page access** in Authentication settings. You will also need to provide a **Group attribute name**. This should match the name of the SAML or OIDC attribute containing the group name in your Identity Provider.

![](https://www.datocms-assets.com/55802/1719587896-screenshot-2024-06-28-at-16-17-56.png)

When the setup is completed, you will be prompted to terminate all ongoing sessions on your tenant. This is advised as the groups memberships will not be refreshed for each user until a new session is started.

Finally, you can [configure specific pages to use SSO-group-based-page-access](https://www.tines.com/docs/pages/distribution-and-access-control#controlling-end-user-access).

### Login recovery codes

Recovery codes provide a secure way to regain access to your Tines account if you lose access to your regular login method. Each code can only be used once and is automatically invalidated after use.

They can be useful to regain access to your account if your authentication system is unavailable or misconfigured. For example, if your SSO settings are incorrect, your identity provider is having issues, or you are unable to access your email account.

> **TIP:** Before configuring single sign-on for the first time or modifying single sign-on settings, we recommend you generate a set of recovery codes to regain access to your account in case you get locked out.

## Setting up recovery codes

Only tenant owners have the ability to generate recovery codes, and they can only be used by active users. To set up recovery codes, follow these steps:

1. Ensure the "User recovery codes" feature is turned on for your tenant in Authentication settings.
2. Navigate to the User page
3. Use the banner at the top or find your user profile
4. Click "Generate recovery codes"
5. Save the displayed codes in a secure location

**Important**: The codes will only be shown once and cannot be recovered if lost. Store them securely before closing the modal. They can be regenerated at any time.

## Using recovery codes

When you need to use a recovery code, visit the `https://<tenant-domain>/login/recovery` page. Enter one of your saved recovery codes in the field and click "Continue". If the code is valid, you'll be automatically logged into your account. Remember that each code can only be used once - after successful use, it will be automatically invalidated.

> **IMPORTANT:** If a user has been deactivated (either by a tenant owner or via user provisioning), their login recovery code will not work. This is to guard against purposely deactivated users from logging into a tenant.

## Security considerations

The security of your recovery codes is paramount. We recommend storing them in a secure password manager or on another trusted secure storage solution. Never share your recovery codes with anyone, as they provide direct access to your account. You should immediately deactivate or regenerate codes if you suspect they're compromised.

As a tenant owner, you have control over the tenant's recovery codes. You can view which users have recovery codes enabled directly from the users list, and you have the ability to delete recovery codes for any user if necessary.

Every attempt (successful or otherwise) to login using recovery codes will be recorded in an audit log.

### User administration

## User management

Click on your team name in the menu on the upper left side and select "Users" (`https://<tenant-domain>/settings/users`). You will be presented with a list of users, their current status, tenant role, and last active time. To view additional information about a user, click on the user's name in the list. From here, you can edit a user's profile.

**

## Invite a user

To invite a user to Tines, click "New user" from the Manage users page. Enter the invitee's email address, pressing 'enter' or 'comma' key to add. From here, you can assign a team to the user (Optional) and choose the role they should have.

![](https://www.datocms-assets.com/55802/1747736064-invite.png)

When you're ready, you can click "Send invites". An invite email will be sent with instructions on how the invitee can complete their registration.

Learn more about [Record #emkj3IWcQP6A5ykwsKOupg.](https://www.tines.com/docs/user-provisioning/)

## Permissions

### Tenant roles

There are two tenant user roles in Tines:

- `User`
- `Tenant owner`

User accounts can only perform actions on objects owned by a team they are a member of. Tenant owners can perform actions on any object, regardless of the team that owns it, including other users' drafts.

### Tenant permissions

Users who are not tenant owner can be assigned individual tenant permissions to perform tenant-wide operations, such as managing the tenant's tunnels.

| **Name** | **Description** |
| --- | --- |
| AUDIT\_LOG\_READ | 

Read all audit logs in the tenant



 |
| 

FEATURE\_FLAG\_MANAGE



 | 

Manage feature flags for the tenant



 |
| 

TUNNEL\_MANAGE



 | 

Manage tunnels and command-over-http



 |

Only tenant owners have permission to:

1. Completely remove a user from a tenant.
2. Delete queued or retrying jobs.

Learn more about [team administration here](https://explained.tines.com/en/articles/6883947-using-teams-in-tines), such as how to add users to a team, or learn more about [team user roles here.](https://hub.tines.com/docs/admin/teams)

#### Automated user provisioning

### User provisioning options

You can configure your tenant to use JIT or SCIM for user provisioning, instead of manually inviting users and assigning them to specific teams and roles.

You can choose from the following options:

- **Default user provisioning**: Invite users directly and assign them to teams and roles manually.
- **SCIM provisioning**: Automatically sync users and groups from your identity provider. Best for centralized, admin-managed provisioning.
- **Just-in-time user provisioning**: Create user accounts automatically the first time someone signs in with SSO. Best for simpler setups without automated user sync.
  
  - **Enhanced Just-in-time syncing**: In addition to automated user provisioning upon first login, also update user permissions using the latest information from the identity provider on subsequent logins.

Please note that manual user invitations may be disabled under certain circumstances:

- When [JIT](https://www.tines.com/docs/jit/) with "**Enhanced Just-in-time syncing"** is enabled, since users are provisioned just-in-time and synced with the IdP on subsequent logins. 
- When [SCIM](https://www.tines.com/docs/scim/) is enabled, since users are created automatically by the IdP using the SCIM protocol.
- When the "**Restrict user invitations to tenant owners**" setting is enabled, only tenant owners can send out invites.

### User group mappings

In order for users to be granted permissions automatically, tenant owners can configure a mapping between IdP groups and Tines permissions in the tenant's Authentication Settings. 

> **IMPORTANT:** These mappings will be used to assign users to teams, roles and permissions. When using JIT, the mappings will only be applied on the first sign in of a user. When using "enhanced just-in-time syncing", users will be updated every time they log in according to the most recent mappings and the user's latest group memberships. When using SCIM, users will be kept in sync any time a SCIM operation is performed or the mappings are updated.

For example, the following would assign users in the `Administrators` group to be Tenant Owners, and members of several groups to join specific teams with different roles and permissions:

```json
{
  "tenant_owners_groups": ["Administrators"],
  "mappings": [
    { "group_name": "Administrators", "team_name": "Analytics", "role_name": "TEAM_ADMIN" },
    { "group_name": "Managers", "team_name": "Analytics", "role_name": "TEAM_ADMIN" },
    { "group_name": "Managers", "team_name": "Incident Response", "role_name": "EDITOR" },
    { "group_name": "Analysts", "team_name": "Analytics", "role_name": "EDITOR" },
    { "group_name": "Everyone", "team_name": "Incident Response", "role_name": "VIEWER" }
  ],
  "tenant_permissions": [{ "group_name": "Managers", "permission": "AUDIT_LOG_READ" }]
}
```

#### **Mapping team memberships**

In order for your Idenitity provider groups to be mapped into [Tines Teams](https://www.tines.com/docs/teams/) and [Case Groups, ](https://www.tines.com/docs/case-groups/)you need to configure a list of correspondences between IdP groups and Tines Teams via `mappings`.  

This will specify how the users from the target IdP group are mapped into the destination Tines Teams or Case Groups, as well as the Role they will be assigned.

Each entry in the `mappings` array must have:

- `group_name` field with the *source *name of an IdP group from your identity provider.
  
  - Note: `group_name` used to be called `sso_group`. This syntax is deprecated.
- `team_name` corresponding to a *destination* Tines Team or Case Group. Case sensitive.
- `role_name` is an existing Tines team role (`VIEWER`, `EDITOR`, `TEAM ADMIN`, `CASE_MANAGER` or a custom role). Role names are case-insensitive.

> **INFO:** Although the mapping key is `team_name`, the value can refer to either the Team name or the Case Group name.

In scenarios where a user is a member of more than one source IdP group that is mapped to a multiple roles on the same team or case group, the first applicable entry in the list will be used.

For example:

```json

{
  "mappings": [
    { "group_name": "Managers", "team_name": "Analytics", "role_name": "TEAM_ADMIN" },
    { "group_name": "Managers", "team_name": "Incident Response", "role_name": "EDITOR" },
    { "group_name": "Analysts", "team_name": "Analytics", "role_name": "EDITOR" },
    { "group_name": "Everyone", "team_name": "Incident Response", "role_name": "VIEWER" }
  ]
}
```

In this case, if a user belongs to the `Everyone` and to the `Managers` source IdP Groups, they would get the `EDITOR` role in the Incident Response team since that is stated at the top of the list.

#### **Mapping tenant owners**

In the example above, the `tenant_owners_groups` lists an IdP group (called "Administrators") that should get promoted to "Tenant Owner" . When `tenant_owners_groups` is configured, any existing users who are Tenant Owners and do not belong to a group listed here will be downgraded to regular user. Please make sure that group memberships are being synchronized correctly before making changes to this field.

- Note: `tenant_owners_groups` used to be called `tenant_owners_group` (and support one group name only). This syntax is deprecated.

#### **Mapping tenant permissions**

The `tenant_permissions` field can be used to assign [tenant permissions](/docs/admin/user-administration/#permissions) to IdP groups.

#### JIT

## Just-in-time user provisioning

With SSO enabled via SAML or OIDC, administrators can optionally enable just-in-time user provisioning. When enabled, administrators can provide a json configuration mapping a group on their SSO identity provider to a Tines team and role. Once setup, a user signing into Tines for the first time will automatically be placed in the designated team and role mapped to their SSO group in the configuration json without requiring an invitation to the tenant.

Note: A change to the just-in-time configuration will not update existing users' teams and roles unless `Enhanced Just-in-time syncing` is enabled (see below for details).

> **NOTE:** JIT support is not available in all plans, please reach out to your Tines point of contact or [Tines support](mailto:support@tines.com) to learn more about enabling it.

To setup:

1. Configure your tenant to use SAML or OIDC for single sign-on
2. Enter a value for "SSO-group-based access", so that the user group information is available to Tines when a user logs in.
3. Select Just-in-time user provisioning in the User provisioning section.
4. Configure a group mapping that has at least one entry for `mappings`, and optionally `tenant_owners_groups` and `tenant_permission`. See the [Automated user provisioning section](https://www.tines.com/docs/admin/user-administration/user-provisioning/#user-group-mappings) for details about configuring mappings.

![](https://www.datocms-assets.com/55802/1677088092-screen-shot-2023-02-22-at-9-47-27-am.png)

*An example of a Group Attribute Statement configuration in Okta.*

**Optional Mappings**

Optional: For a new user's first name, last name, and avatar to be automatically configured when signing on, the givenname, surname, and avatar attributes can be added to the SAML statement or OIDC claim.

> **TIP:** In order to ensure your Identity Provider is pushing groups and memberships to Tines correctly, you can review the "Identity Provider Groups" for a user in the admin users list and confirm that expected groups are present.

## Enhanced Just-in-time syncing

As an addition to just-in-time user provisioning administrators can also enable 'Enhanced Just-in-time syncing'. This feature enables syncing of a users teams, case groups and roles from their IdP on *every login. *

Once this is enabled on a tenant, a users team memberships and roles are provisioned and synced in Tines to match changes made to resources on the Identity Provider. **As a result, managing a user's team and role assignments via Tines will now be disabled. **

> **IMPORTANT:** If you are using the \`tenant\_owners\_groups\` configuration to automatically manage which users are granted "tenant owner" status via multiple groups, you must first ensure the groups listed for this are assigned in the Identity Provider and are passed along to Tines. Otherwise, when setting \`tenant\_owners\_groups\`, current admins who are not associated with the correct group could lose their privileges.

To setup: (at `/settings/authentication` in your tenant)

1. Follow steps for configuration of JIT above. This feature is compatible with existing JIT configurations so current mappings will work.
2. Scroll down to the User provisioning section. Click on the Switch entitled "**Enhanced just-in-time syncing**".
3. Save
4. Now on each login a user's team and role assignments will be synced from their IdP settings.

![](https://www.datocms-assets.com/55802/1760710456-jit_sync.png)

*Authentication Settings configuration of enhanced JIT syncing.*

#### SCIM

[SCIM](https://scim.cloud/) allows you to configure an Identity Provider (IdP) to synchronize users with your Tines tenant. 

The Tines API offers a set of SCIM v2-compliant endpoints, documented [here.](https://www.tines.com/api/scim)  Our own API for provisioning a tenant's user group mapping is documented [here](https://www.tines.com/api/admin/scim_user_group_mapping).

> **NOTE:** SCIM support is not available in all plans, please reach out to your Tines point of contact or [Tines support](mailto:support@tines.com) to learn more about enabling it.

## Enabling SCIM

To turn SCIM on or off for your tenant, go to "Authentication settings" in the settings menu. Note that SCIM is independent from [SSO](https://www.tines.com/docs/single-sign-on/) (even though you will probably use the same Identity Provider for both), and is not compatible with [Just-in-time user provisioning](/docs/admin/single-sign-on#just-in-time-user-provisioning).

If you enable SCIM for your tenant, users can only be added and modified via SCIM. Regular methods of inviting and modifying users (via the UI or the API) will be disabled and users can only be managed by the Identity Provider.

> **TIP:**
> Before setting up SCIM, you need to make sure that the SCIM configuration will not revoke your tenant owner status immediately upon saving.
> 
> As long as your IdP is not passing in a userType value other than TENANT\_OWNER (or is not passing any userType value), you can set up SCIM without a tenant\_owners\_group and map the tenant owners group later.
> 
> However, if your IdP is going to pass a userType value that is not TENANT\_OWNER, one way to do this is to set up [JIT](https://www.tines.com/docs/admin/user-administration/jit/) first (temporarily), to make sure the identity provider group that you will be configuring to have tenant owner status is read by Tines. For example, if you have a group called "Admins" in your identity provider pased in the "Group" property, you would set up JIT with the following JIT config:
> 
> { "group\_attribute\_name": "Group", "tenant\_owners\_groups": \["Admins"\], "mappings": \[\] }
> 
> After this change, you can verify your user is part of the group in your user profile after logging in. Then, you and all users that belong to this group would remain as tenant owners after you set up the SCIM config with a tenant\_owners\_groups field

## Configuring your Identity Provider

In order to configure your Identity Provider to synchronize users with Tines you will need to configure the following:

- Base URL: `https://<<META.tenant.domain>>/api/scim/v2`
- Authorization: Bearer token, with a tenant-level API key
- Unique identifier field for users: `userName` (note: Tines requires that the userName is the user's email) 

### Operations

Supported operations:

- Provisioning Users and Groups.
- Pushing Profile Updates.
- Adding/removing Users from Groups
- Deprovisioning Users.
  
  - Note: some Identity Providers may not fully remove users once they are deactivated, destroyed or removed from the application, and instead will mark them as `active: false`. While these users will no longer be able to access the Tines tenant, a Tenant Owner must delete them via the Tines UI or API to remove their data from the system.

> **TIP:** All SCIM operations show up in Audit Logs. You can use these to see exactly what your Identity Provider is sending Tines and ensure that Users and Groups are being assigned and mapped as expected.

### Attribute mapping

Refer to the [API docs](https://www.tines.com/api/scim#supported-attributes) for the full list of User attributes supported by Tines.

In order to grant users the "Tenant Owner" role in Tines, you can map a field in your user profile to the `userType` field in the Tines application in your IdP. If you configure SCIM to sync profile attributes, users without this `userType` will lose their admin privileges. Alternatively, you can enable group mapping (see following section), in which case the `userType` attribute is not used.

For example, in Okta, assuming there is an `admin` field in the User profile, add a mapping from Okta users to Tines of: `(user.admin == true) ? 'TENANT_OWNER' : ''` → `userType`

### Identity Provider Group to Tines permissions mapping

> **TIP:** Before configuring the mapping, ensure your Identity Provider is pushing groups and memberships to Tines. You can review this by inspecing the badges that show up below the name in a user's profile.

If you configure group mappings as described in [automated user provisioning](https://www.tines.com/docs/user-provisioning/), users will be assigned to the teams, roles and permissions as configure in the mapping rules, and updated any time there is a SCIM operation invoked by the IdP or a change is made to the mappings.

#### Custom roles

# Introduction

Custom roles are administrator-defined permissions to access a team. 

All custom roles start from the Tines default roles: viewer, builder, manager. A user can have multiple roles in a tenant, but only one role per team. 

You can see more information about team roles [here](https://www.tines.com/docs/admin/teams/#roles).

> **TIP:** For example: Diane might have read and write permissions in Team A, but, only have view permissions for cases in Team B.

You can turn on and off individual features for that user role. 

---

## Creating a custom role

Administrators own creating custom roles. 

[](https://demo.arcade.software/UY8nEVk55IoEKFXTO4VT?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)



1. Navigate to `Roles` within the admin dropdown
2. Choose the **New Role** button on the bottom right corner of the modal
3. Name the role
4. Give a description, this appears below the role for other users
5. Customize the feature level permissions (see the feature table below)
6. Click `Create` in the bottom left of the modal

> **NOTE:**
> A maximum of 25 custom roles can be created per tenant.
> 
> Custom roles are part of our Enterprise Tenant Management features and aren't available for all plans. Please reach out to your Tines point of contact to learn more about adding them to your plan.

## Updating custom roles

Administrators can make updates to custom role permissions. 

To do this, they: 

1. Navigate to `Roles` within the admin dropdown
2. Click the role you want to edit
3. Change the permissions
4. Click `Update`

As a reminder, you cannot change the role name, only the permissions.

## Feature permission list

These features allow granualar access. They are binary (on or off). The table details what permissions enabling the feature allows. Below is a brief overview of those permissions. 

- **`View`** permissions allow a builder to see the information, but not interact with it. 
- `**Write**` permissions allow a builder to update the information, but not perform destructive or permissive actions (add members, change permissions, etc.). `Create` & `update` permissions also fall under this category.
- **`Manage`** permissions allow the builder to move, change permissions, or perform destructive actions. This should be limited to roles where a builder can perform destructive actions. `Delete` permissions also fall under this category.

Note: Assigning a less restricted permission for a feature will not allow that role to inherit the more restricted permissions - e.g., assigning a Manage type permission will not grant the role the Edit and View type permissions for that feature, so when adding a Manage permission you will typically want to add the Edit and View permission for that object type as well.

### Stories

| **Feature** | **Sub feature** | **Permission** | **Notes** |
| --- | --- | --- | --- |
| **Actions** | Run script | Create | 

Create Run script actions.

*Also requires Story update permissions.*

 |
|   | Run script | Update | 

Update Run script actions.

*Also requires Story update permissions.*

 |
|   | Run script | Delete | 

Delete Run script actions.

*Also requires Story update permissions.*

 |
|   | Event Transform in automatic mode | Create/Update | 

Create and update Event Transform actions in automatic mode.

*Also requires Story update permissions.*

 |
|   | Event Transform in automatic mode | Delete | 

Delete Event Transform actions in automatic mode.

  
*Also requires Story update permissions.*

 |
| **Change Control** |  | Manage | 

Toggle change control.

Review change requests.

Set change control notifications webhook.

 |
|  |  | Review | Review change requests. |
| **Credentials** |  | Manage | Move, Delete credentials. |
|  |  | Update | Update credentials. |
| **Events** |  | Manage | 

Delete events.

Delete story data.

 |
| **Folders** |  | Create | Create folders. |
|  |  | Manage | Delete folders. |
| **Page Themes** |  | Create | Create page themes. |
|  |  | Manage | Delete page themes. |
|  |  |  |  |
| **Resources** |  | Manage | Delete, Move resources. |
|  |  | Update | Update resources. |
| **Stories** |  | Create | Create and import stories. |
|  |  | Manage | 

Delete, Import, Move stories.

Approve change requests.

Delete logs and events.

 |
|  |  | Run | Run stories. |
|  |  | Update | Update stories. |

### Cases

Cases is built on granular permissions for every component within a case. See [roles and permissions](https://www.tines.com/docs/roles-and-permissions/) in cases for more information.

| Feature | Permission | Notes |
| --- | --- | --- |
| **Actions** | Create | Add an action to a case. |
|  | Delete | Remove an action from a case. |
|  | Run | Run an action on a case. |
|  | Update | Update an action on a case. |
| **Assignees** | Update | Add or remove assignees from a case. |
| **Blocks (Notes)** | Create | Create a note block. |
|  | Delete | Delete all note blocks. |
|  | Update | Update all note blocks. |
| **Cases  
** | Bulk manage cases | Manage cases in bulk. Requires additional "update" permissions for each case component a user is allowed to update in bulk. |
|  | Create | Create a new case. |
|  | Delete | Delete a case. |
|  | View | View all cases and case content. |
|  | Update | Update case name and basic fields. |
|  | Update security settings | 
Update team-level case security settings

 |
|  | View sensitive information | View case fields marked as sensitive. |
| **Closure conditions** | Override closure conditions | Override closure conditions whenever updating a case or changing status. |
| **Comments** | Create | Add a comment to a case. |
|  | Delete | Delete your authored comments only. Delete any comment if granted the "Cases > Manage" permission. |
|  | Update | Update your authored comments. Update any comment if granted the "Cases > Manage" permission. |
| **Comment reactions** | Create | Add a reaction to a comment. |
|  | Delete | Remove a reaction from a comment. |
| **Description** | Update | Update case description content. |
|  | Update task status | Mark a task as complete or incomplete within a case description. |
| **Fields** | Create | Create new case field definitions. |
|  | Delete | Delete case field definitions. |
|  | Update | Update existing case field definitions. |
| **Field values** | Create | Add an existing field to a case. |
|  | Delete | Remove a field from a case. |
|  | Update | Update or reset field values on a case. |
| **Files** | Create | Attach a file to a case. |
|  | Delete | Delete files you've attached to a case. Delete any files if granted the "Cases > Manage" permission. |
| **Links** | Create | Link a case to another case. |
|  | Delete | Unlink cases from each other. |
| **Metadata** | Update | Update metadata values. |
| **Priority** | Update | Change the priority of a case. |
| **Records** | Create | Add a record to a case. |
|  | Delete | Remove a record from a case. Removing a record from a case does not delete the record itself. |
| **Saved views** | Create | Create a saved view in Cases. |
|  | Delete | Delete a saved view in Cases. |
|  | Update | Update a saved view in Cases. |
| **SLAs** | Create | Create SLA rules on a case. |
|  | Delete | Delete SLA rules from a case. |
|  | Update | Update SLA rules on a case. |
| **Status** | Create | Create new case status definitions. |
|  | Delete | Delete case status definitions. |
|  | Transition | Change a case's status value. |
|  | Update | Update existing case status definitions. |
| **Subscribers** | Create | Subscribe users to a case. |
|  | Delete | Unsubscribe users from a case. |
| **Tags** | Update | Add or remove tags on a case. |
| **Tasks** | Assign tasks | Assign a task to one or more users within the team or case group. |
|  | Complete tasks | Mark tasks complete or incomplete. |
|  | Create tasks | Add tasks to a case, case template, or "create case" story action. |
|  | Delete tasks | Delete tasks from a case. |
|  | Edit tasks | Change the name of an existing task. |
|  | View tasks | View tasks within a case. |
| **Templates** | Create | Create new case layout templates. |
|  | Delete | Delete case layout templates. |
|  | Update | Update existing case layout templates. |
| **Webhooks** | Create | Create webhooks on a case. |
|  | Delete | Delete webhooks from a case. |
|  | Update | Update webhooks on a case. |

### AI

| **Feature** | **Permission** | **Notes** |
| --- | --- | --- |
| **Presets** | Create | Author Workbench presets. |
|   | Run | Use Workbench presets.  
  
Any credentials that are configured on the preset will work but people with this permission cannot access those credentials directly. |

### Records

| **Feature** | **Permission** | **Notes** |
| --- | --- | --- |
| Records | View | View record types, record reports and records. |
| Records | Create | Add new records. |
| Records | Update | Update record field values. |
| Records | Update record report filters | Update the filters of a record report. |
| Records reports | Manage | Create, Update, Delete, Export record reports.  
*  
This is specific to the **records reports** section* |

### Other

| **Feature** | **Permission** | **Notes** |
| --- | --- | --- |
| **Read** | View | Gives the user the ability to read all things within a team. In order to view cases, records, dashboards, and/or stories, please be sure to enable this |
| **Teams** | Manage | 
Delete a team.

Edit, delete, move page collections.

Add, remove team members.

Manage team member roles

 |
| **Templates** | Create | 

Create templates within a team.

 |
|   | Update | 

Update templates within a team.

 |
|   | Delete | 

Delete templates within a team.

 |
|   | Manage | 

Manage templates within a team, or share with other teams.

 |



## Permission templates

To help get started, you can choose to start from a team role template. Then customize from there. This auto-enables the [team RBAC](https://www.tines.com/docs/admin/teams#team-roles) feature permissions. 

| **Permission template** | **Definition** |
| --- | --- |
| **Viewer** | 
If you choose this template, any feature permission not enabled will default to **read-only** access.

*Note: any feature disabled on another user role defaults to view-only for that role.*

 |
| **Editor** | This permission grants **read and write** access for the enabled features within the team. |
| **Team admin** | 

This permission grants **read and write** access for the enabled features within the team. This includes the ability to ***perform destructive actions,*** such as deleting.

 |
| **Workbench user** | 

This permission grants access to **use configured presets** on the team. Any **credentials that are configured on the Workbench preset may be used** **only through Workbench** but cannot be changed or viewed.

Users cannot access any other part of Tines in that team.

 |

All disabled features default to view-only. 

### Take a look

*Take a look at creating a role*

### Audit logs

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

We automatically capture an audit log any time a user changes any piece of data in your Tines tenant.

These audit logs are available to tenant admins and users with the `AUDIT_LOG_READ` permission via the UI and API. Information about the operations logged can be found in our [API docs.](https://www.tines.com/api/audit-logs/)

The retention period for audit logs in your Tines tenant is two years.

## Exporting audit logs

We currently support scheduled exports of audit logs to[ Amazon S3](https://aws.amazon.com/s3/) buckets, which runs every 15 minutes. Only tenant admins can configure this setting.

**

#### **Credentials for Amazon S3**

To securely invoke Amazon S3 APIs, Tines supports AWS authentication using [assumed roles](https://www.tines.com/docs/credentials/aws/).

##### **Required IAM permissions**

To allow exports to S3 from Tines, the IAM role must include the following permission:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3::[bucket-name]/*"
        }
    ]
}
```

### Teams

## Using Teams

The Teams feature of Tines allows for logical separation of users, credentials, resources, and stories. Team members will only be able to access the configuration items of teams that they are a part of unless they are a tenant owner.

Team members can be added using the "Invite" button on the "Members" page of the team. If an account does not exist yet in the tenant when added to a team, an invitation email will be sent to the user to join the tenant. Members can be removed from a team at any point by any team administrator or a tenant owner, unless they are the last member of the Team.

See [User administration](https://www.tines.com/docs/user-administration/) for more details about user invitations.

## Roles

All members of a team or [group](https://www.tines.com/docs/case-groups/) must be assigned a role in Tines, e.g., Team Admin, Editor or Viewer. Each role has a set of permissions assigned to them which will give read and or write access to objects in the team or group. 

> **IMPORTANT:** A user can only have one role per team; roles cannot be combined to grant additional permissions.

#### Team Admin

This role gives the user unrestricted read and write permissions to all objects in the team. This is useful for admins or team managers responsible to adding/removing users from a team. This will also give them permission to perform destructive actions on a team such such as deleting stories, resources or credentials. 

A team admin can change the role of a user or assign a role to a new user on the team `members` list.

![](https://www.datocms-assets.com/55802/1718896016-team-roles.png)

#### Editor

This role gives the user read and write permissions to most objects on the team, but unlike the team admin role, they cannot perform destructive actions on the team such as deleting stories, resources or credentials. 

This role is useful for most users who simply want to create, edit and run stories in Tines. 

#### Viewer

This role gives the user read-only permission to most objects on the team. 

This role useful for situations where a colleague or auditor needs oversight or understanding of an automated workflow, without the ability to alter it – or even accidentally break it.

#### Case manager

*This role is only available to customers with cases enabled.*

This role gives the user read-write permissions to cases, while restricting access to other objects on the team (stories, resources, credentials, events, etc.).

This role is useful for users who work with cases and do not need access to many other aspects of the system.

Note that while Case managers are not able to access most objects in their team, they are still able to author stories in their drafts and access objects (resources, credentials, stories for "send to story") shared globally with all users.

#### Dashboard manager

This role gives the user permission to create, edit, and view dashboards on the team, while restricting access to other objects (stories, resources, credentials, events, etc.). It also allows the user to use Workbench presets shared on the team.

This role is useful for users who build and maintain dashboards but do not need broader access to the team.

#### Dashboard viewer

This role gives the user read-only access to dashboards on the team, with no permission to create or edit them. Access to other objects on the team is also restricted.

This role is useful for stakeholders or auditors who need visibility into dashboard data without the ability to alter it.

#### Workbench user

This permission grants access to use configured presets on the team. Any credentials that are configured on the Workbench preset may be used only through Workbench but cannot be changed or viewed.

Users cannot access any other part of Tines in that team.

### Permissions table

<table border="1" style="border-collapse: collapse;"><tbody><tr><td><strong>Permission/Team Role</strong></td><td><strong>Team Admin</strong></td><td><strong>Editor</strong></td><td><strong>Viewer</strong></td><td><strong>Case manager</strong></td><td><strong>Workbench user</strong></td></tr><tr><td>Read all objects (stories, resources, credentials, events, etc.)</td><td>x</td><td>x</td><td>x</td><td></td><td></td></tr><tr><td>Read cases</td><td>x</td><td>x</td><td>x</td><td>x</td><td></td></tr><tr><td>Create / update stories</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Create / update credentials*</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Create / update resources</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Create/ update templates</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Create / update cases</td><td>x</td><td>x</td><td></td><td>x</td><td></td></tr><tr><td>Create / update records</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Create / update dashboards</td><td>x</td><td>x</td><td></td><td>x</td><td></td></tr><tr><td>Run an action</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Import / export stories</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Publish / push stories</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Manage team members (add / remove)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Share objects (stories, resources, credentials, templates)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Move objects (stories, resources, credentials)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Delete objects (stories, resources, credentials, events, records)</td><td>x</td><td></td><td></td><td></td><td></td></tr><tr><td>Delete templates</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Author Workbench presets</td><td>x</td><td>x</td><td></td><td></td><td></td></tr><tr><td>Use Workbench presets</td><td>x</td><td>x</td><td></td><td></td><td>x</td></tr></tbody></table>

*Sensitive components of a credential that could increase risk of malicious credential exposure have further restrictions:

- **Access **(control where the credential can be used): changes are restricted to Team Admin.
- **Domains** (Specify allowed domains, URL paths, or server hosts to use restricted credentials in outbound requests): changes are restricted to Team Admin or the credential creator.

## Sharing across teams

By default, objects (credentials, resource, templates and send to story enabled stories) can be shared across teams.

This allows you to securely reuse the object in multiple places. If it's updated in one team, it's updated across teams. Please note: all credentials, resources and send to story enabled stories must have a unique name across the tenant. 

Credentials and resources can be shared by using the "Access" configuration options within the object. Sharing is enabled within the team the credential or resource exists by default, with the option of "All teams" available to make the item available to all teams within the tenant, with selected teams and/or current team's members' personal teams who have story run permissions in the current team.

Similarly, when send to story is enabled, you can share a story by: opening that story, opening the **send to story settings** modal from the right hand panel, and selecting teams from the "Access**" **section. This allows other teams to use this story in their send to story actions. 

See also [Send to Story: Enabling a story for Send to Story](https://www.tines.com/docs/stories/send-to-story#enabling-a-story-for-send-to-story-creating-a-sub-story) and [Send to Story: Access](https://www.tines.com/docs/stories/send-to-story#access).

**Custom object sharing**

If enabled on your tenant, you can determine with which teams an object is shared. You'll see the option to select teams individually when you open the access configuration options on any of the objects: credentials, resources, or stories with send to story enabled. 

## Team story allocation

If you have multiple teams in a tenant, the tenant owner can provision story limits by team. 

1. Go to your Team dropdown menu in the top left corner
2. Click on `Settings`
3. Choose `Story allocation` from the secondary popover
4. Check the box for the team(s) you want to limit
5. Set the maximum stories for the team(s)
6. Click `Save`

You can always come back and modify these settings. 

**

## Team event limits

Tenant owners can configure daily event limits for teams, as well as notifications when teams are approaching or have reached their event limits. Read more about configuring team event limits in the [event limit settings](https://www.tines.com/docs/event-limit-alerts/) documentation.

### Tenant-wide credentials

Tenant owners can access `https://your-tenant-domain/settings/credentials` to view and manage credentials across their entire tenant. This includes credentials from users' drafts.

**

The table view, similar to the team-level credentials, offers more information at a glance: the number of actions and stories the credential is used in, the credential type, when it was last used, created, and updated, the team name, the folder name, and the author name. 

![](https://www.datocms-assets.com/55802/1729522062-tenant-wide-credentials-context-menu.png)

The right click context menu or the row level actions allows tenant owners to view a specific credential in its team context, move it between teams or folders, and delete it permanently. 

## Filtering

Credentials can be filtered by usage, domain restriction, and team.

![](https://www.datocms-assets.com/55802/1729521904-tenant-wide-credentials-filters.gif)

**Unused in actions**: When auditing credentials, tenant owners may filter credentials by Unused in actions to find unused credentials.

**Restricted** or **Unrestricted**: Tenants created before May 17, 2024 12:00 UTC will have the abilty to filter by Restricted and Unrestricted credentials, to help tenant owners implement [domain restriction](https://www.tines.com/docs/domain-restriction/).

**Team**: Since this table view includes team names at a glance, it is also possible to filter by Team.

## Bulk deletion

![](https://www.datocms-assets.com/55802/1729522144-tenant-wide-credentials-bulk-delete-warning-modal.png)

Tenant owners can select multiple credentials to delete them. Before confirming deletion, make sure to double check the warning modal for any credentials still in use.

### Page access

Tenant owners can access `https://your-tenant-domain/settings/pages` to view and manage pages across their entire tenant. This includes pages from users' drafts.

[](https://demo.arcade.software/AvaU4vPOZ94Sw4H8Jlcu?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true)

This table view, while similar to the team-level pages table, allows tenant owners to see more information at a glance: the page's action name, its parent story, which team it belongs to, the access control level, and its creator. 

![](https://www.datocms-assets.com/55802/1756921746-screenshot-2025-09-03-at-18-48-57.png)

The right click context menu or the row level actions allows tenant owners to view the page in the storyboard, view the live page, or edit the page in the page editor. 

## Sorting and filtering

Pages can be sorted by usage, or filtered by access control level and team.

![](https://www.datocms-assets.com/55802/1756921827-screenshot-2025-09-03-at-18-50-15.png)

**Recently created** or **Recently updated**: Sort pages by when it was created or updated.

**Access control**: Filter pages by its [access control level](/docs/pages/distribution-and-access-control/#controlling-end-user-access). This is particularly helpful for reviewing all pages' access control levels after [restricting the "Anyone with the link" access control option](/docs/pages/distribution-and-access-control/#restricting-end-user-access-options).

**Team**: Since we display the team the page belongs to, you can also filter pages by its team.

### Custom certificate authority

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.



Visit **Settings **> **Custom Cert Authorities **or`https://<tenant-domain>/settings/custom_ca` to configure a certificate authority for use by all of your IMAP and HTTP Request actions. Enter the PEM encoded X.509 public certificate (or certificate chain) for your custom certificate authority and hit save. Alternatively, you can also create and manage dedicated custom certificate authorities from the same UI. See our What's New post [here](https://www.tines.com/whats-new/multiple-custom-certificate-authorities-is-here).

Your custom certificate authority will then be used in addition to the standard public certificate authorities when validating the certificates of the IMAP and HTTP endpoints your actions are contacting. You can also configure AI providers to use the custom CA when connecting to private AI services.

### Custom sender email addresses

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

Custom sender email addresses can only be configured by a tenant owner.

Self-hosted customers can configure custom sender emails, but need to talk with the team in your organization that manages your SMTP server to assist.

The default sender email address for emails sent with the email action is `mail@tines.io`.

### Configuring a custom sender email address

Visit **Settings **> **Sender emails **or `https://<tenant-domain>/settings/sender_email_addresses` and click the "+ Add Address" button, as shown in the top right of the below screenshot.

![Custom sender email address admin page with no custom sender email addresses added](https://www.datocms-assets.com/55802/1699969784-screenshot-2023-11-14-at-13-49-32.png)

![Add a sender email address modal with two fields, the first for an email address and the second for a mail from domain. The bottom of the modal has a button with the text "Save email address"](https://www.datocms-assets.com/55802/1699969987-screenshot-2023-11-14-at-13-52-50.png)

In this form you will need to enter:

1. The email address you wish to use (e.g., [user@example.com](mailto:user@example.com))
2. The MAIL FROM domain (MAIL FROM domain must be the a subdomain of the domain using in the email address, e.g., tinesmail.example.com for example.com)

> **WARNING:** You won't be able to use an no-reply or donotreply email (e.g., no-reply@example.com) as this could cause issues with deliverability due to their email reputation.

The resulting screen will provide two DNS records, a MX record and a TXT record, that will need to be configured in your DNS provider, and you will receive a verification email. Please note that DNS verification may take up to 72 hours to propagate.

If you're adding multiple custom sender emails, you'll have to configure each of their DNS records (MX and TXT) separately. However, if you are adding multiple custom sender emails with the same domain (e.g., acme.com as the domain, and support.acme.com and legal.acme.com as the custom sender emails), you can only add one MX record for that domain, as multiple MX records will not verify successfully. See [AWS docs](https://docs.aws.amazon.com/ses/latest/dg/mail-from.html#mail-from-set) for more information.

> **NOTE:** Tines custom sender email supports SPF but not DKIM at this time.

![DNS records that need to be added for a custom sender email address with example "user@example.com"](https://www.datocms-assets.com/55802/1713872124-dns-records-for-spf-custom-email-sender.png)



### Using a custom sender email address

Once verification is complete, select the send email action and add the "Custom sender address" from the "+ Option" menu. You can now enter the email in the "Custom sender address" field.



![The build panel of an email action with the custom sender address section highlighted](https://www.datocms-assets.com/55802/1713872141-custom-sender-address.png)

> **NOTE:** Actions using Custom Sender Address will error if the address has not been verified.

### Login notice

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.



Tenant owners can now configure a custom notice that must be accepted by all tenant users each time they log in. This can be used to meet compliance requirements and to implement controls such as those included in FedRAMP®.

## Configuring a login notice

To configure this, visit the admin page for this at `https://<tenant-domain>/admin/login_notice` and set the contents of the notice:

![Image showing the content of /admin/login_notice for configuring a login notice. Contains a text box and a submission button](https://www.datocms-assets.com/55802/1720792935-screenshot-2024-07-12-at-14-59-11.png)

Markdown is supported for the notice. To unset a login notice, set the text box to empty and save it. 

Once a login notice is set, users will be redirected to the login notice each time they sign in. Users will have to accept the notice or sign out. An example of the login notice:

![Sample login notice that shows a header, some text, a sign out button and an accept button](https://www.datocms-assets.com/55802/1720793175-screenshot-2024-07-12-at-15-00-21.png)

### IP access control

> **IMPORTANT:** This feature is only available on dedicated cloud tenants.

Tenant owners can configure an IP allowlist to restrict access to the tenant to registered addresses only.

This can be configured at [https://<your-tenant>.tines.com/settings/ip_access_control](https://<your-tenant>.tines.com/admin/ip_access_control)

Rules must specify an address or CIDR range and a description. Both IPv4 and IPv6 addresses are supported. The format for CIDR ranges is `address/mask` .

Rules can also be fetched and modified via the [Tines API](https://www.tines.com/api/admin/ip_access_control/).

> **WARNING:** You must add your current network before enabling IP access control. Otherwise, you will be immediately locked out from the tenant. Please ensure that at least one tenant owner will be able to access the tenant from an allowlisted network at all times, since this is the only way to reach the tenant. If you do get locked out, please reach out to Tines Support for assistance.

### Mode of operation

There are two modes of operation available:

- Tenant and API: enforce access control on the tenant UI and API access. That is, on all endpoints that require user authentication (except SSO-protected pages).
- Everything: enforce access control on the tenant UI, API access, all webhooks and public pages. That is, on private and public tenant endpoints. Please note this rule is global and applies to all resources.

In both modes of operation, requests coming from within the tenant itself (e.g. an HTTP Request Action calling the Tines API) are always allowed.

> **INFO:** If you have SSO configured, you don't need to add your identity provider's IP addresses to the allowlist. Similarly with tunnels, adding Cloudflare's IP addresses is unnecessary.

### Logging

If your tenant has IP access control enabled then you can optionally turn on logging to view blocked access requests. The logs contain the timestamp, IP address, user agent and URL of the blocked request. They can be found on the IP access control settings page and expire after 90 days. Logs can be filtered by searching for an exact IP address or user agent.

**

### Action egress control

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

Tenant owners can configure an allowlist with rules for IPs or FQDNs to restrict access to the destinations that HTTP Request Actions are allowed to make requests to, and/or to restrict the domains that Send Email Actions are allowed to send emails to. **Note:** for Send Email Actions, only an allowlist for FQDNs is supported, not IPs. 

> **IMPORTANT:** Action egress control rules also apply to [HTTP request type credentials](https://www.tines.com/api/credentials/create-http-request/).

Rules must specify an address or CIDR range for IP or FQDN, and a description. Both IPv4 and IPv6 addresses are supported. Only an IP or FQDN should be provided, but not both. The format for CIDR ranges is `address/mask` . By default FQDNs are resolved to IPs so if an FQDN is not on the allowlist but resolves to an IP that is on the allowlist then the request will be allowed, this setting can be turned off.

Once the rules are enabled, any outbound requests from HTTP Request Actions that does not match the IP or FQDN will fail at run time and the relevant error can be found in the logs. Similarly, any email sent from a Send Email Action to a domain that does not match a FQDN on the list will fail. 

Rules can also be fetched and modified via the [Tines API](https://www.tines.com/api/admin/action_egress_control_rules/).

**

### Tunnel egress control

Action egress control can be turned on for HTTP request actions that use a tunnel by enabling the option on the action egress control settings page. Note that FQDNs will not be resolved to IPs for tunnelled requests so if a FQDN isn't on the allowlist then the request will not be allowed regardless of whether the IP it resolves to is on the allowlist or not. See the settings options below:

![Action egress control rules settings page](https://www.datocms-assets.com/55802/1749559950-screenshot-2025-06-10-at-13-52-21.png)

### Custom domains

Tines allows customers to either rename their entire tenant to a new domain, or add a custom domain specifically for webhooks and pages.

## Tenant renaming

> **NOTE:** To change your tenant name or request a custom domain, reach out to your CSM, CSE or [Tines Support](https://www.tines.com/contact-support/).

Renaming a tenant allows you to use the new name through the UI, webhooks, pages, API, etc. An example of a tenant rename would be changing it from its autogenerated name (e.g., `https://rough-water-2650.tines.com`) to domain that is more meaningful to your business (e.g., `https://acme.tines.com`).

### Tenant rename with single sign-on

When renaming a tenant with single sign-on configured, you must follow these steps:

1. Disable single sign-on.
2. Change webhook and pages usages
  
  - It is possible to continue to use the old domain for webhooks and pages, as long as you request that it is added as a custom domain.
3. Coordinate with your customer success manager (CSM) or Tines Support and confirm your tenant was renamed.
4. Sign in to your renamed tenant and re-enable single sign-on using the new domain.

> **INFO:** If user email domains need to be changed as well, you can change them between the disable and enablement of single sign-on.

## Custom domain

Custom domains **only apply to webhooks and pages**. They will not affect anything else, like the UI. Sharing webhook and page links on a familiar domain can help build trust with your recipients.

If you have a custom domain, you can access pages and webhooks using both the custom domain and the tenant domain.

Find more information on custom domains for pages [here](https://www.tines.com/docs/pages/custom-domain-for-pages/).

## Tines Workbench Slack App

If you have the [Tines Workbench Slack](https://www.tines.com/docs/workbench/workbench-in-slack/) app installed and your tenant is renamed, you will need to [reinstall the Slack app](https://www.tines.com/docs/workbench/workbench-in-slack/). The Workbench Slack app routes messages based on your tenant domain, and a rename will cause it to stop responding until the app is reinstalled.

### Tunnel

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

## Tunnel Setup

The Tunnel feature of Tines provides a method to access your systems running on private networks from the Tines cloud environment, securely. Tunnel is deployed as a container service.

To enable the Tunnel feature, contact [Tines support](mailto:support@tines.com). Once enabled, visit `https://<tenant-domain>/admin/tunnel` to obtain the information needed for deploying the container.

> **Note:** Tines Tunnel is not supported for self-hosted deployments.

## Deploying a Tunnel

<table border="1" style="border-collapse: collapse;"><tbody><tr><td colspan="3">Container requirements</td></tr><tr id="210f4837-78fe-8091-881e-f7e25ba70812"><td id="ZjQG" class="">Image</td><td id="AV~n" class="">tines/tines-tunnel</td><td id="L]h@" class=""></td></tr><tr id="210f4837-78fe-8029-8d05-dc619f827a4a"><td id="ZjQG" class="">Container count</td><td id="AV~n" class="">1</td><td id="L]h@" class=""></td></tr><tr id="210f4837-78fe-8068-8fd8-c8e6bcd85c5e"><td id="ZjQG" class="">RAM</td><td id="AV~n" class="">2 GB</td><td id="L]h@" class="">per container</td></tr><tr id="210f4837-78fe-80cb-950b-c332f284335a"><td id="ZjQG" class="">Disk</td><td id="AV~n" class="">2 GB</td><td id="L]h@" class="">per container</td></tr><tr id="210f4837-78fe-8051-9a04-d1bfe4a0844e"><td id="ZjQG" class="">Architecture</td><td id="AV~n" class="">x86_64</td><td id="L]h@" class=""></td></tr><tr id="214f4837-78fe-8096-a118-c188aed51124"><td id="ZjQG" class="">Logging</td><td id="AV~n" class="">Log rotation considered.</td><td id="L]h@" class="">Tines containers write logs to STDOUT/STDERR by default, so customers should capture and rotate these logs to mitigate disk storage and performance issues.</td></tr></tbody></table>

The Tunnel container can be run with Docker. Here we provide an example of configuration using `docker compose` . You can test your tunnel setup using `docker run` but we recommend managing this setup using `docker compose` or a similar orchestration tool.

```bash
docker run \
  --name tines-tunnel \
  --env TINES_TUNNEL_SECRET="secret" \
  tines/tines-tunnel:latest
```

First create the `docker-compose.yml`

```yaml
version: '3.9'

services:
  tines_tunnel:
    image: tines/tines-tunnel:latest  # Make sure to use the correct image name and tag
    ports:
      - "9000:9000"  # This maps the container's port 9000 to the host's port 9000
    environment:
      TUNNEL_METRICS_PORT: "9000"
      TINES_TUNNEL_SECRET: "secret"
    deploy:
      mode: replicated
      replicas: 1
    
```

> **NOTE:** Port 9000 is not required, it is used for [health check metrics](https://www.tines.com/docs/admin/tunnel/health-check-metrics/)

Next run the container using the following command

```bash
docker-compose up -d
```

The Tunnel service will utilize the routing and DNS services of the host it is deployed to.

**Using Docker compose secrets:**

If you wish to, Tines Tunnel supports the use of Docker secrets to store the Tines Tunnel secret.
Firstly, we need to save the `TINES_TUNNEL_SECRET` in a file:

```bash
echo "your tunnel secret" > tines_tunnel_secret.txt 
```

Then, setup your `docker-compose.yml` like so:

```yaml
version: "3.9"

services:
  tines_tunnel:
    image: tines/tines-tunnel:latest # Make sure to use the correct image name and tag
    ports:
      - "9000:9000"  # This maps the container's port 9000 to the host's port 9000
    secrets:
      - TINES_TUNNEL_SECRET
    environment:
      TUNNEL_METRICS_PORT: "9000"
    deploy:
      mode: replicated
      replicas: 1

secrets:
  TINES_TUNNEL_SECRET:
   file: "./tines_tunnel_secret.txt"
```

**Configuration for High Availability:**

In addition to the health check configuration via docker compose, you can configure a second instance of tines-tunnel on another server with the `same secret` & docker-compose. **The steps for this are the same as above.**

If you want to distribute traffic between the separate server tunnels, you can set up a load balancer (e.g., Nginx, HAProxy) in front of these servers.

### Ensuring Tines Tunnel Container Starts After Server Restart

To ensure the Tines Tunnel container automatically starts after a server or service restart, follow the recommendations below for your deployment method.

**Docker**

```bash
docker run --restart unless-stopped \
  --name tines-tunnel \
  --env TINES_TUNNEL_SECRET="secret" \
  tines/tines-tunnel:latest
```

**Docker compose**

Add the restart key to your service definition in `docker-compose.yml`

```yaml
version: '3.9'
services:
  tines_tunnel:
    image: tines/tines-tunnel:latest
    ports:
      - "9000:9000"
    environment:
      TUNNEL_METRICS_PORT: "9000"
      TINES_TUNNEL_SECRET: "secret"
    restart: unless-stopped
```

## Using a Tunnel

HTTP Requests can be sent through the Tunnel by using the *"Use tunnel"* parameter in the configuration of a HTTP Request Action.

> **NOTE:** Tunnels don't currently work with NTLM authentication.

![](https://www.datocms-assets.com/55802/1759147646-tunnel.png)

AI traffic can also be sent through the Tunnel by using the *"Use tunnel"* parameter when configuring a custom AI provider.

![](https://www.datocms-assets.com/55802/1757071816-tunnel-ai.png)

You can also use [command-over-http](https://www.tines.com/docs/admin/command-over-http/) with HTTP requests to make programatic calls to your private network.

## Connectivity Requirements

Tunnel uses [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) and requires connectivity to Cloudflare to function properly.

![Diagram of Tines Tunnel (docker container) and how it interacts between your systems.](https://www.datocms-assets.com/55802/1697542895-screenshot-2023-10-17-at-6-40-55-am.png)

*An overview of how the tunnel interacts with your network. *

No inbound connectivity to the container is required by Tines from the internet or elsewhere. The container will attempt to form a connection with the services below from its deployment location.

| DESTINATION | PORT | PROTOCOLS | DIRECTION | Purpose |
| --- | --- | --- | --- | --- |
| [region1.v2.argotunnel.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#required-for-tunnel-operation) | 443,7844 | TCP/HTTPS | Outbound | Establishes a secure tunnel for outbound connections to Cloudflare's network. |
| [region2.v2.argotunnel.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#required-for-tunnel-operation) | 443,7844 | TCP/HTTPS | Outbound | Establishes a secure tunnel for outbound connections to Cloudflare's network. |
| [api.cloudflare.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#optional) | 443,7844 | TCP/HTTPS | Outbound | Accesses Cloudflare's API for management tasks (not required for core functionality). |
| [update.argotunnel.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#optional) | 443,7844 | TCP/HTTPS | Outbound | Checks and downloads updates for the Argo Tunnel service. |
| [<your-tenant-name>.tines-tunnel.com](tines-tunnel.com) | 443,7844 | TCP/HTTPS | Outbound | Default tunnel - Facilitates secure, outbound-only connections to Tines services. |
| [<your-tunnel-name>-<your-tenant-name>.tines-tunnel.com](tines-tunnel.com) | 443,7844 | TCP/HTTPS | Outbound | Note: If you have more than one tunnel, then each new tunnel will be prefixed with the tunnel name.  
  
Facilitates secure, outbound-only connections to Tines services. |

For more information, see Cloudflare's documentation [here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/do-more-with-tunnels/ports-and-ips/).

## Firewall Rules

Ensuring that `cloudflared`, which runs inside the Tines tunnel image, can connect to Cloudflare’s global network on port 7844, your firewall must allow outbound connections to the destinations on port 7844 via TCP to the IPs mentioned by Cloudflare [here](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#required-for-tunnel-operation) and are listed below.

| DOMAIN | IPv4 | IPv6 | PORT | PROTOCOLS |
| --- | --- | --- | --- | --- |
| region1.v2.argotunnel.com | `198.41.192.167`  
`198.41.192.67`  
`198.41.192.57`  
`198.41.192.107`  
`198.41.192.27`  
`198.41.192.7`  
`198.41.192.227`  
`198.41.192.47`  
`198.41.192.37`  
`198.41.192.77` | `2606:4700:a0::1`  
`2606:4700:a0::2`  
`2606:4700:a0::3`  
`2606:4700:a0::4`  
`2606:4700:a0::5`  
`2606:4700:a0::6`  
`2606:4700:a0::7`  
`2606:4700:a0::8`  
`2606:4700:a0::9`  
`2606:4700:a0::10` | 7844 | TCP/UDP (`http2`/`quic`) |
| region2.v2.argotunnel.com | `198.41.200.13`  
`198.41.200.193`  
`198.41.200.33`  
`198.41.200.233`  
`198.41.200.53`  
`198.41.200.63`  
`198.41.200.113`  
`198.41.200.73`  
`198.41.200.43`  
`198.41.200.23` | `2606:4700:a8::1`  
`2606:4700:a8::2`  
`2606:4700:a8::3`  
`2606:4700:a8::4`  
`2606:4700:a8::5`  
`2606:4700:a8::6`  
`2606:4700:a8::7`  
`2606:4700:a8::8`  
`2606:4700:a8::9`  
`2606:4700:a8::10` | 7844 | TCP/UDP (`http2`/`quic`) |
| \_v2-origintunneld.\_tcp.argotunnel.com1  | Not applicable | Not applicable | 7844 | TCP (`http2`) |
| cftunnel.com1  | Not applicable | Not applicable | 7844 | TCP/UDP (`http2`/`quic`) |
| h2.cftunnel.com1  | Not applicable | Not applicable | 7844 | TCP (`http2`) |
| quic.cftunnel.com1  | Not applicable | Not applicable | 7844 | UDP (`quic`) |

1 This rule is only required for firewalls that enforce SNI.

## Health statuses

Commonly encountered health statuses for Tunnel and any remediations. These health statuses indicate the health of the connection between the container and Cloudflare Edge. It doesn't indicate the health of the container itself. 

### `Healthy`

The tunnel is active and serving traffic through four connections to the Cloudflare global network.

### `Degraded`

One or more tunnel containers are active and serving traffic, but at least one individual connection has failed. Further degradation in tunnel availability could risk the tunnel going down and failing to serve traffic.

We recommend restarting the tunnel container to resolve the issue. If the status continues to be in a degraded state after restart, please reach out to support with the debug logs. You can find the instructions on how to turn on debug logs for your tunnel container by visiting `/admin/tunnel` in your Tines tenant.

### `Inactive`

This usually happens when tunnels have been created, but have never been run. It should clear away after your first run.

### `Down`

One or more tunnel containers cannot serve traffic as it has no connections to the Cloudflare global network. Please ensure that the tunnel container is up and running.

## Tunnel Permissions

Tenant owners or users with the `TUNNEL_MANAGE` tenant permission can restrict which team(s) can use a tunnel from the link `https://<tenant-domain>/admin/tunnel`. The default for all existing and new tunnels is that any team can access them until an admin chooses otherwise.

To configure team access select the tunnel from the list after navigating to `/admin/tunnel.`

Select the teams which you would like to have access to the tunnel and click **save**.

![](https://www.datocms-assets.com/55802/1715075390-screenshot-2024-05-07-at-10-49-32.png)

#### Installing the Tines Tunnel​ on AWS Fargate

> **NOTE:** This step is only needed if you want to make requests to internal services from a cloud Tines tenant.

### **Step 1. Prepare the tines-tunnel Docker image**[**​**](https://www.tines.com/docs/self-hosting/onboard/aws-fargate-deployment#b1-prepare-the-tines-tunnel-docker-image)** for upload to AWS ECR**

Again, to make things a little easier, we copy the image from Docker Hub into an ECR repository:

```bash
aws ecr create-repository --repository-name tines-tunnel

# Replace this with the address of the registry output in the previous command:
REGISTRY=306378194054.dkr.ecr.eu-west-1.amazonaws.com

aws ecr get-login-password --region eu-west-1 | \
  docker login --username AWS --password-stdin $REGISTRY

TUNNEL_IMAGE=tines-tunnel:latest

docker pull tines/$TUNNEL_IMAGE
docker tag tines/$TUNNEL_IMAGE $REGISTRY/$TUNNEL_IMAGE
docker push $REGISTRY/$TUNNEL_IMAGE
```

### **Step 2. Create and upload the .env file to S3**[**​**](https://www.tines.com/docs/self-hosting/onboard/aws-fargate-deployment#b2-create-and-upload-the-env-file-to-s3)** **

```bash
# Replace this with your own unique bucket name:
ENV_FILE_S3_BUCKET=tines-tunnel-env

# Replace this with the name of AWS region you're running Tines in:
AWS_REGION=eu-west-1

aws s3api create-bucket \
  --bucket $ENV_FILE_S3_BUCKET \
  --region $AWS_REGION \
  --create-bucket-configuration "LocationConstraint=$AWS_REGION"

aws s3api put-public-access-block \
  --bucket $ENV_FILE_S3_BUCKET \
  --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

cat << EOF > tines-tunnel.env

# Copy this variable from https://<your-tenant>.tines.com/admin/tunnel
# Make sure to remove any quote characters from the value
TINES_TUNNEL_SECRET=...

EOF

aws s3 cp tines-tunnel.env s3://$ENV_FILE_S3_BUCKET/tines-tunnel.env
```

### **Step 3. Create the IAM roles for running the containers**[**​**](https://www.tines.com/docs/self-hosting/onboard/aws-fargate-deployment#b3-create-the-iam-roles-for-running-the-containers)** **

```bash
# This only needs to be create once for an AWS account - if you're already using ECS, you can skip this command:
aws iam create-service-linked-role \
  --aws-service-name ecs.amazonaws.com

aws iam create-role \
  --role-name tinesTunnelTaskExecutionRole \
  --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [{ "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" }]}'

aws iam attach-role-policy \
  --role-name tinesTunnelTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

# Replace this with the name of the bucket you created in step B2:
ENV_FILE_S3_BUCKET="tines-tunnel-env"

aws iam put-role-policy \
  --role-name tinesTunnelTaskExecutionRole \
  --policy-name TinesEnvAccess \
  --policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::'$ENV_FILE_S3_BUCKET'/tines-tunnel.env"]}, { "Effect": "Allow", "Action": ["s3:GetBucketLocation"], "Resource": ["arn:aws:s3:::'$ENV_FILE_S3_BUCKET'"]}]}'
```

### **Step 4. Prepare a security group**[**​**](https://www.tines.com/docs/self-hosting/onboard/aws-fargate-deployment#b4-prepare-a-security-group)** **

```bash
aws ec2 create-security-group \
  --group-name tines-tunnel \
  --description "tines-tunnel container security group for the Tines Tunnel application"
```

### **Step 5. Create the ECS task definitions and cluster**[**​**](https://www.tines.com/docs/self-hosting/onboard/aws-fargate-deployment#b5-create-the-ecs-task-definitions-and-cluster)

```bash
aws logs create-log-group --log-group-name tines-tunnel

# Replace this with the ARN of the role you created in step B3:
EXECUTION_ROLE_ARN=arn:aws:iam::306378194054:role/tinesTunnelTaskExecutionRole

# Replace this with the address of the registry created in step B1:
REGISTRY=306378194054.dkr.ecr.eu-west-1.amazonaws.com

# Replace this with the name of the image you added to the registry in step B1:
IMAGE=tines-tunnel:latest

# Replace this with the name of the bucket you created in step B2:
ENV_FILE_S3_BUCKET=tines-tunnel-env

# Replace this with the name of AWS region you're running Tines in:
AWS_REGION=eu-west-1

aws ecs register-task-definition \
  --family "tines-tunnel" \
  --memory 2048 \
  --network-mode awsvpc \
  --cpu 1024 \
  --execution-role-arn $EXECUTION_ROLE_ARN \
  --container-definitions '[{"name": "tines-tunnel", "image": "'$REGISTRY'/'$IMAGE'", "environmentFiles": [{"value": "arn:aws:s3:::'$ENV_FILE_S3_BUCKET'/tines-tunnel.env", "type": "s3"}], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "tines-tunnel", "awslogs-region": "'$AWS_REGION'", "awslogs-stream-prefix": "tines" }}}]'

aws ecs create-cluster --cluster-name tines-tunnel
```

### **Step 6. Start the service**[**​**](https://www.tines.com/docs/self-hosting/onboard/aws-fargate-deployment#b6-start-the-service)** **

```bash
# Replace these with the IDs of two subnets from your VPC:
SUBNET_IDS=subnet-606e7706,subnet-bd5bb2f6

# Replace this with the ID of the tines-tunnel security group created above:
SECURITY_GROUP_ID=sg-01eb5f237d80b8458

aws ecs create-service \
  --cluster tines-tunnel \
  --service-name tines-tunnel \
  --task-definition tines-tunnel:1 \
  --desired-count 1 \
  --launch-type "FARGATE" \
  --network-configuration "awsvpcConfiguration={subnets=[$SUBNET_IDS],securityGroups=[$SECURITY_GROUP_ID],assignPublicIp=ENABLED}"
```

### Automatic Restarts with AWS Fargate

**Note:** When you deploy the Tines Tunnel using an ECS Service on AWS Fargate (as described above), the service will automatically ensure that the tunnel container is always running. If the container or underlying infrastructure stops or restarts for any reason, ECS will automatically launch a new instance to maintain the desired count. No additional configuration is required for automatic restarts, this is handled by the ECS Service itself.

#### Health check & metrics

## Tunnel health checks & metrics

### Health check

Health checks are handled internally within the application, to automatically restart the tunnel container in the event it enters an unhealthy state.

You can also run a health check on the tunnel using the following steps if you wish, but this is **optional**:

**Step 1: Exec into the container**

```bash
docker exec -it <container-id> /bin/sh
```

**Step 2: Run tunnel-cf-healthcheck.sh **

```bash
/home/tines/tunnel-cf-healthcheck.sh
```

### Metrics

You can view tunnel metrics from inside or outside the docker container via the `/metrics` endpoint. If you need to override the default port which is `9000` you can run the tunnel container with the env var `TUNNEL_METRICS_PORT`.

**Option 1: Run the container via docker-compose.yml and exec into container**

```bash
version: '3.9'

services:
  tines_tunnel:
    image: tines-tunnel:latest  # Make sure to use the correct image name and tag
    environment:
      TUNNEL_METRICS_PORT: "9000"
      TINES_TUNNEL_SECRET: "secret"
    deploy:
      mode: replicated
      replicas: 2  # Deploy two replicas per host
```

```bash
docker-compose up -d
```

```bash
docker-compose exec tines_tunnel /bin/sh
```

You can use tools such as cURL, wget, or others to fetch the metrics from the tunnel inside of the container.

```bash
curl http://0.0.0.0:9000/metrics
```

![](https://www.datocms-assets.com/55802/1716388725-screenshot-2024-05-22-at-15-38-25.png)

**Option 2: Expose the /metrics endpoint outside the container**

The `-p` option is **required** to publish the port to the host. This value is needed whether or not you are overriding the `TUNNEL_METRICS_PORT `e.g `-p 0.0.0.0:${TUNNEL_METRICS_PORT}:<container_port>`

See `docker-compose.yml` example

```bash
version: '3.9'

services:
  tines_tunnel:
    image: tines-tunnel:latest  # Make sure to use the correct image name and tag
    ports:
      - "9000:9000"  # This maps the container's port 9000 to the host's port 9000
    environment:
      TUNNEL_METRICS_PORT: "9000"
      TINES_TUNNEL_SECRET: "secret"
    healthcheck:
      test: ["CMD", "/home/tines/tunnel-cf-healthcheck.sh"]
      interval: 30s
      timeout: 10s
      retries: 3
    deploy:
      mode: replicated
      replicas: 2  # Deploy two replicas per host
```

See `docker run` example

```bash
docker run \
  -d \
  --name tines-tunnel \
  --env TUNNEL_METRICS_PORT=9000 \
  --env TINES_TUNNEL_SECRET="YOUR_TUNNEL_SECRET" \
  -p 0.0.0.0:9000:9000 \
  tines/tines-tunnel:latest
```

Once the above configuration is in place, you can use tools such as cURL, wget, or others to fetch the metrics from the tunnel outside of the container.

```bash
curl http://0.0.0.0:9000/metrics
```

> **INFO:** You can also use a [restart policy](https://docs.docker.com/engine/containers/start-containers-automatically/#use-a-restart-policy) to make the container restart automatically (e.g. in the event of the host machine restarting): \`\--restart unless-stopped\`.

#### Troubleshooting and common errors for tunnels

## Troubleshooting

> **NOTE:** Error messages display the original tunnel name under which it was created as opposed to any custom subdomains.

### Container connectivity

Check that your container is active. For example, if you're using Docker, you can use the following command to check the status and how long it's been up:

```bash
docker ps
```

### Container logs

You can also tail the logs to check for any errors. Here's another Docker example:

```bash
docker logs <container name or container id> --tail all
```

If there isn't enough detail, you can specify the log level you need using the environment variable `TUNNEL_LOGLEVEL` when starting a container. We recommend turning off the debug logs, since the debug logs can contain sensitive information. The levels correlate to [Cloudflare tunnel log levels.](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/tunnel-run-parameters/#loglevel)

```bash
docker run \
  --env TINES_TUNNEL_SECRET="secret" \
  --env TUNNEL_LOGLEVEL=debug \
  tines/tines-tunnel:latest
```

### Tunnel connectivity

Check that your tunnel is healthy using the Settings page that lists [tunnel health statuses](https://www.tines.com/docs/admin/tunnel#health-statuses).

> **INFO:** The number next to the tunnel status represents total active client connections for the tunnel. See more info [here](https://explained.tines.com/en/articles/10922763-what-does-the-number-signify-next-to-the-tunnel-status-on-the-tunnel-configurations-page).

### Action configuration

Check that your action is configured with the correct URL to your private network system by using `curl` from within your container.

If you can see an error in the action log, check the common errors below to help diagnose the issue.

## Tunnel Troubleshooting Script

If your tunnel container successfully starts but is not connecting, run the provided `tunnel-troubleshoot.sh`. Start an interactive bash session in the Tunnel container and execute `/home/tines/tunnel-troubleshoot.sh.` The script will check for connectivity issues, proxies, tunnel credentials, and container metrics. You should see an output for each test and recommendations to follow if there are any failures:

```bash
Cloudflare Tunnel Connectivity Troubleshooter
Timestamp: 2026-03-11 16:42:10 UTC
1. DNS SRV Resolution (edge server discovery)
  PASS: SRV record resolved
    target=region1.v2.argotunnel.com.  port=7844
    target=region2.v2.argotunnel.com.  port=7844
2. Edge Connectivity (port 7844, IPv4)
  PASS: region1.v2.argotunnel.com:7844 TCP open
  PASS: region2.v2.argotunnel.com:7844 TCP open
3. Edge TLS Handshake (port 7844, IPv4)
  PASS: region1.v2.argotunnel.com:7844 TLS OK
  PASS: region2.v2.argotunnel.com:7844 TLS OK
  IPv6: not available (not required — cloudflared uses IPv4 by default)
3b. TLS Interception Check
  Certificate issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
  PASS: Direct connection to Cloudflare (no TLS interception)
4. Cloudflare API (port 443)
  PASS: api.cloudflare.com reachable (HTTP 404)
5. Proxy Configuration
  HTTP_PROXY:  <unset>
  HTTPS_PROXY: <unset>
  NO_PROXY:    <unset>
6. Tunnel Credentials
  PASS: Secret present via file /run/secrets/TINES_TUNNEL_SECRET (184 bytes)
7. Metrics Endpoint
  readyConnections: 4 (from http://127.0.0.1:9000/ready)
  PASS: Tunnel has 4 active connections
---
Passed: 10  Failed: 0
```

### Run the troubleshooter at startup

If you can't easily exec into the tunnel container (for example, in locked-down Kubernetes clusters), you can run the troubleshooting script automatically at container startup by setting the ``TUNNEL_TROUBLESHOOT_ON_START`` environmentvariable to ``true``:

```
docker run
--env TINES_TUNNEL_SECRET="secret"
--env TUNNEL_TROUBLESHOOT_ON_START=true
tines/tines-tunnel:latest
```

When enabled, the diagnostic output appears in the container logs with a`[STARTUP-DIAG]` prefix, making it easy to filter:

```
docker logs | grep "STARTUP−DIAG"
```

This is off by default and does not affect tunnel startup — if any diagnostic check fails, the tunnel still starts normally.

## Common Errors

Commonly encountered errors when using a tunnel in your `HTTP Request` or `Send Email Action` and how to troubleshoot.

### `TUNNEL_CONNECTION_ERROR`

We were unable to establish a tunnelled connection for `tunnelName`

**This may be an issue with either the endpoint being called from the action** **request or the tunnel being down.**

Please check the following:

1.  Your `tines-tunnel` container is active.
2. The tunnel is properly configured and healthy.
3. The action request endpoint used is a valid hostname and url.

You can see the health status of your tines tunnel by visiting `/settings/tunnel` and viewing the tunnel status.

### `TUNNEL_CONNECTION_ERROR: Exception encountered is: Connection reset by peer - SSL_connect`

We were unable to establish a tunnelled connection to the tunnel container or the remote origin behind the tunnel container. This error likely indicates that the TCP/TLS handshake between Tines and the remote origin failed. To remediate the issue please check the following:

1.  Your tines-tunnel container is active.
2. The tunnel is properly configured and healthy.
3. You are able to send a `curl` or similar to the remote origin from within the tunnel container.
4. The action request endpoint used is a valid hostname and url.

You can see the health status of your tines tunnel by visiting `/settings/tunnel` and viewing the tunnel status. If the issue persists, please reach out to our support team.

### `TUNNEL_OPEN_TIMEOUT_ERROR`

We encountered a timeout while opening the tunnel connection for `tunnelName.`

**This may be an issue with either the endpoint being called from the action** **request or the tunnel being down.**

Please check the following:

1.  Your `tines-tunnel` container is active.
2. The tunnel is properly configured and healthy.
3. The action request endpoint is a valid hostname and url.

You can see the health status of your tines tunnel by visiting `/settings/tunnel` and viewing the tunnel status.

If the tunnel is configured correctly, the server may not be responding promptly.

To troubleshoot further:

1. Copy your Tines Action as a curl command and run it in your container (on your server).
2. If it works, it's likely a timeout issue. Increase the timeout on your HTTP Request Action to at least 60 seconds.

### `SSL_ERROR`

An SSL connection error was returned for the request.

SSL certificate verification failed. This may be caused by an outdated SSL certificate, incorrect URL, network issues, or misconfigurations.

To resolve, check the following:

1. Renew or Update SSL Certificate: Ensure it's current.
2. Check URL and Parameters of action request are correct.
3. Network Configuration: Inspect for issues.
4. Disable SSL verification temporarily: Consider setting `disable_ssl_verification` to `true`, in the action options.

> **NOTE:** When a request through a tunnel fails with SSL certificate validation errors, we may need to [add a custom certificate authority (CA)](https://www.tines.com/docs/admin/custom-certificate-authority/) to the tenant trust store. This is common in environments that use internal public key infrastructure (PKI)or TLS-inspecting proxies that re-sign certificates. As a rule of thumb, if setting `disable_ssl_verification=true` makes the request succeed, the long-term fix is to import the correct custom CA and then re-enable SSL verification

### `TINES_TUNNEL_SECRET environment variable has an invalid value.`

This might happen if you're using [Docker secrets to store variables](https://www.tines.com/docs/self-hosting/additional-applications/). Check that you're setting a `secret-env` at `/run/secrets/secret-env.`

### `unknown protocol`

If the tunnel is failing to connect to Cloudflare servers, you can try switching to a different protocol using the  [https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/tunnel-run-parameters/#protocol](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/tunnel-run-parameters/#protocol). For some customers, quic does not work but http2 does.

#### Running the Tines Tunnel using Docker

## Deploying Tunnel

The Tunnel container can be run with Docker using the example below or similarly with other container orchestration platforms.

```bash
docker run \
  --env TINES_TUNNEL_SECRET="secret" \
  tines/tines-tunnel:latest
```

The Tunnel service will utilize the routing and DNS services of the host it is deployed on.

## Using Tunnel

HTTP Requests can be sent through the Tunnel by utilizing the *"Use tunnel"* parameter in the configuration of a HTTP Request Action.

![](https://www.datocms-assets.com/55802/1656002731-admin_tunnel_configuration-4790132adf7240ea2b9b5c635f0bf543.png)

## Connectivity Requirements

Tunnel uses [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) and requires connectivity to Cloudflare to function properly.

No inbound connectivity to the container is required by Tines from the internet or elsewhere. The container will attempt to form a connection with the services below from its deployment location.

| DESTINATION | PORT | PROTOCOLS | DIRECTION | Purpose |
| --- | --- | --- | --- | --- |
| [region1.v2.argotunnel.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#required-for-tunnel-operation) | 443,7844 | TCP/HTTPS | Outbound | Establishes a secure tunnel for outbound connections to Cloudflare's network. |
| [region2.v2.argotunnel.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#required-for-tunnel-operation) | 443,7844 | TCP/HTTPS | Outbound | Establishes a secure tunnel for outbound connections to Cloudflare's network. |
| [api.cloudflare.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#optional) | 443,7844 | TCP/HTTPS | Outbound | Accesses Cloudflare's API for management tasks (not required for core functionality). |
| [update.argotunnel.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/#optional) | 443,7844 | TCP/HTTPS | Outbound | Checks and downloads updates for the Argo Tunnel service. |
| [<your-tunnel-name>-<your-tenant-name>.tines-tunnel.com](tines-tunnel.com) | 443,7844 | TCP/HTTPS | Outbound | Facilitates secure, outbound-only connections to Tines services. |
| [registry.hub.docker.com](https://registry.hub.docker.com/r/tines/tines-tunnel) | 443,7844 | TCP/HTTPS | Outbound | Required to pull the Tines Tunnel image from Docker Hub. |

> **INFO:** The requirements listed here apply to your firewall configuration so ensure these outbound connections are allowed.

For more information, see Cloudflare's documentation [here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/do-more-with-tunnels/ports-and-ips/).

#### Verifying the tunnel image

The Tines tunnel image can be verified with [cosign](https://github.com/sigstore/cosign/releases/). Verifying this way guarantees that the image was built in our Github actions from our main, protected branch.

```bash
cosign verify \
    --certificate-oidc-issuer https://token.actions.githubusercontent.com \
    --certificate-identity-regexp '^https://github.com/tines/tines/\.github/workflows/[^@]+@refs/heads/main$' \
    "tines/tines-tunnel:latest" 
```

You should then get a response:

```
Verification for index.docker.io/tines/tines-tunnel:latest --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates
```

### Command-over-HTTP

> **NOTE:** Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.

Command-over-HTTP allows your Tines tenant to make programmatic calls to systems running on your private network, which may not have HTTP interfaces.

It is deployed as a container service within your network, and has the capability to execute **python**, **bash**, and **powershell** scripts there.

# Deployment

Command-over-HTTP is available with the tunnel add-on option for cloud customers, and included for self-hosted customers. To get started, [reach out to us](https://www.tines.com/contact-support) and we will enable for your tenant.

Once enabled, visit `https://<your-tenant-domain>/admin/command-over-http` to obtain your deployment credentials. Only tenant owners or users with the `TUNNEL_MANAGE` tenant permission can access this page.

The container can be run through various orchestration platforms, for example Docker:

```bash
docker run \
  -d \
  --name command-over-http \
  --env TINES_TUNNEL_SECRET="secret" \
  tines/command-over-http:latest
```

For self-hosted customers, this can be configured without the TINES_TUNNEL_SECRET variable to communicate within your hosted platform setup and not via Cloudflare.

```bash
docker run \
  -d \
  --name command-over-http \
  tines/command-over-http:latest
```

# Usage

Once enabled and deployed, use the [HTTP request action](https://www.tines.com/docs/http-request/) to call the module. To do this, enable the `‘use tunnel’` parameter, and target the special URL https://command-over-http/run.

Finally, pass a JSON payload containing two keys: 1) `executor` (`python3`, `powershell`, or `bash)` and 2) `script` (the command or script you want to execute).

If you do choose to override the hostname when running the container, ensure the tenant can reach the container at the correct hostname via the HTTP action.

![](https://www.datocms-assets.com/55802/1694716924-screenshot-2023-09-01-at-10-39-36-am.png)

#### Connectivity requirements

Depending on whether or not you self-host your Tines tenant, command-over-HTTP connectivity requirements differ.

In addition to the below requirements, in both settings, connectivity from the command-over-HTTP container to internal systems will also be required to execute commands affecting those systems.

## For self-hosted tenants

In this context, the command-over-HTTP container will require connectivity over port 443 to the Tines self-hosted environment.

## For cloud tenants

In this context, we use [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) under the hood to manage connectivity, and you will need to host the tunnel image on your infrastructure.

For the container deployed on your infrastructure, no inbound connectivity is required. **Outbound-only** connections will be made with the following services from your infrastructure:

<table><tbody><tr><td><strong>Destination</strong></td><td><strong>Port</strong></td><td><strong>Protocols</strong></td></tr><tr><td>region1.argotunnel.com</td><td>7844</td><td>TCP/UDP/QUIC/h2mux</td></tr><tr><td>region2.argotunnel.com</td><td>7844</td><td>TCP/UDP/QUIC/h2mux</td></tr><tr><td>region1.v2.argotunnel.com</td><td>7844</td><td>TCP/UDP/QUIC/h2mux</td></tr><tr><td>region2.v2.argotunnel.com</td><td>7844</td><td>TCP/UDP/QUIC/h2mux</td></tr><tr><td>api.cloudflare.com</td><td>443</td><td>TCP/HTTPS</td></tr><tr><td>updates.cloudflare.com</td><td>443</td><td>TCP/HTTPS</td></tr></tbody></table>

If using strict TLS/SSL inspection, exclude the above HTTPS traffic from the interception policy. Additionally, outbound traffic to *cftunnel.com* needs to be excluded.

For more information, see [Cloudflare's documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/install-and-setup/ports-and-ips/).

#### Additional deployment options

The following examples assume Docker, but similar approaches are possible with other container orchestration mechanisms.

## Deploying with SSL

If you'd like to only allow command-over-http to listen on HTTPS (via SSL), then you can follow these steps. Typically its not needed if command-over-http is running in an air-gapped like network. 

This deployment assumes there is a directory under `/ssl/`, on the machine that is running the `docker run …` command. And, that the directory has `tls.crt` and `tls.key` files in it. Then, when the container boots, command-over-HTTP can look up those files locally in the container, from the volume mapping.

```bash
docker run -it --env TINES_TUNNEL_SECRET="<secret>" \
  --env FORCE_SSL="true" \
  --env TLS_CERT_PATH="/ssl/tls.crt" \
  --env TLS_KEY_PATH="/ssl/tls.key" \
  -v /ssl:/ssl
  tines/command-over-http
```

## Authenticating with Kerberos for PowerShell usage

```bash
docker run -it --env KERBEROS_DEFAULT_REALM="TINES.DEV" \
    --env KERBEROS_KDC_DOMAIN="TINES.DEV" \
    --env KERBEROS_ADMIN_SERVER="TINES.DEV" \
    --env KERBEROS_PRINCIPAL_USER_NAME="administrator" \
    --env KERBEROS_PRINCIPAL_USER_PASSWORD="Password123" \
    --env TINES_TUNNEL_SECRET="TunnelSecret" \
    tines/command-over-http:latest
```

See ‘[Passing secret values to the deployment](#passing-secret-values-to-the-deployment)’ below for how to pass Kerberos credentials in.

## Deploying with custom DNS

```bash
docker run \
  --env TINES_TUNNEL_SECRET="secret" \
  --dns="EnterDNSIP" \
  --add-host="EnterHostFQDN:EnterHostIP" \
  tines/tines-tunnel:latest
```

## Passing secret values to the deployment

In the event a credential vault or secrets need to be passed to the command-over-HTTP container during deployment, you can leverage [Docker Compose secrets](https://docs.docker.com/compose/use-secrets/).

First, you’ll need to define where secrets should be pulled in from. In a directory on the container host, create a file named `secrets.env`. In the file enter the following information:

```bash
KERBEROS_PRINCIPAL_USER_PASSWORD=ENTER_PASSWORD_HERE
```

Replace the value `ENTER_PASSWORD_HERE` with expected credential information, in this example the Keberos service account password. Save the file. Now create a file called `docker-compose.yml` with the following file structure:

```yaml
version: "3.8"
services:
  coh:
    image: tines/command-over-http:latest
    deploy:
      replicas: 1
    environment:
      - KERBEROS_DEFAULT_REALM=ENTER_REALM
      - KERBEROS_KDC_DOMAIN=ENTER_KDC_DOMAIN
      - KERBEROS_ADMIN_SERVER=ENTER_DC_SERVER
      - KERBEROS_PRINCIPAL_USER_NAME=ENTER_USER_ACCOUNT_NAME
    secrets:
      - secret-env
secrets:
  secret-env:
    file: ./secrets.env
```

> **NOTE:**
> Instead of specifying the additional environment variables using the previous `--env` argument with `docker run`, you’ll need to fill in the environment variable information in the `docker-compose.yml` file with Docker Compose.  
>   
> Specifically, the values for `KERBEROS_DEFAULT_REALM`, `KERBEROS_KDC_DOMAIN`, `KERBEROS_ADMIN_SERVER`, and `KERBEROS_PRINCIPAL_USER_NAME` would be required.

To use Docker secrets, [Docker Compose](https://docs.docker.com/compose/install/) must be installed on the system command-over-HTTP is being deployed on. The Docker Compose version must support Dockerfile version 3.8 (i.e Docker Compose v2.3.3).

With the file configured run the following command from the directory in which the `docker-compose.yml` and `secrets.env` files exist:

```bash
docker compose up -d 
```

Once the command-over-HTTP container is created, validate the credentials were passed to the container by running the following command:

```bash
docker compose run coh cat /run/secrets/secret-env
```

The value specified in the secrets file will be returned. This process can also be used for credential vaults as well, so long as the data being read in follows this format:

```bash
KERBEROS_PRINCIPAL_USER_PASSWORD=ENTER_PASSWORD_HERE
```

> **NOTE:** The Docker Compose YAML file may need to be modified based on the credential vault

#### Troubleshooting command-over-http

> **TIP:** Since command-over-HTTP leverages tunnels, the best place to start troubleshooting is the [Troubleshooting and common errors for tunnels](/docs/admin/tunnel/tunnel-faqs) page.

## Common errors

### `**ModuleNotFoundError**`

This indicates that the specified library is not included with command-over-HTTP. We support several libraries like python3, bash, iputils, zlib and powershell, but the library you're trying to use may not be included.

An alternative (available in Cloud tenants only) is using the [Run Python script](https://www.tines.com/docs/actions/templates/templates/frequently-used-templates/run-python-script) since it has fewer limitations on the libraries it can use.

### `ERR_STREAM_PREMATURE_CLOSE`

Your script could be running longer than the HTTP request action is.

1. You can set the `EXECUTION_TIMEOUT` env var on the command-over-HTTP container to be a value greater than the one in their HTTP request timeout. The value is in seconds, so, e.g., it could be set to 600 (10 mins). See the [environment variables page](https://www.tines.com/docs/environment-variables/) for command-over-HTTP for more information.
2. Your command might just be taking longer, so extending the timeout figures on the container and/or private system could help.

#### Environment Variables

This application is configured via ENV variables. Configuration relates to two services: HTTP server application and cloudflared tunnel.

### HTTP server application

By default HTTP server doesn't check `x-api-key` header and doesn't force HTTPS.

<table border="1" style="border-collapse: collapse;"><tbody><tr><td><strong>ENV Variable Name</strong></td><td><strong>Default</strong></td><td><strong>Description</strong></td></tr><tr><td><code>PORT</code></td><td><span>80</span></td><td><span>Server HTTP port</span></td></tr><tr><td><code>TLS_PORT</code></td><td><span>443</span></td><td>Server HTTPS port</td></tr><tr><td><code>FORCE_SSL</code></td><td></td><td>Force usage of HTTPS protocol</td></tr><tr><td><code>API_KEY</code></td><td></td><td><code>x-api-key</code><span>&nbsp;</span>header value to check (not checked by default)</td></tr><tr><td><code>TLS_CERT</code></td><td></td><td>Raw TLS certificate</td></tr><tr><td><code>TLS_CERT_PATH</code></td><td><code>/ssl/tls.crt</code></td><td><span>Path to TLS certificate</span></td></tr><tr><td><code>TLS_KEY</code></td><td></td><td><span>Raw TLS key</span></td></tr><tr><td><span><code>TLS_KEY_PATH</code></span></td><td><code>/ssl/tls.key</code></td><td>Path to TLS key</td></tr><tr><td><span><code>EXECUTION_TIMEOUT</code></span></td><td>300</td><td><span>Timeout (seconds) for command execution</span></td></tr><tr><td><code>KERBEROS_DEFAULT_REALM</code></td><td><code>EXAMPLE.COM</code></td><td><span>The domain equivalent name of the Kerberos realm (all caps)</span></td></tr><tr><td><code>KERBEROS_KDC_DOMAIN</code></td><td><code>kdc.example.com</code></td><td><span>Full KDC domain name</span></td></tr><tr><td><code>KERBEROS_ADMIN_SERVER</code></td><td><code>admin.example.com</code></td><td><span>Full admin server domain name</span></td></tr><tr><td><code>KERBEROS_PRINCIPAL_USER_NAME</code></td><td><code>admin</code></td><td><span>Name of the principal for Kerberos auth</span></td></tr><tr><td><code>KERBEROS_PRINCIPAL_USER_PASSWORD</code></td><td><code>PASSWORD</code></td><td><span>Password of the principal for Kerberos auth</span></td></tr></tbody></table>

### Debug

You can turn on debug logs by setting the following environment variable or passing it to docker run. Example:

```
export DEBUG="*"
```

### Cloudflared

If some of these variables are not set or proper values are not provided, the `tines-tunnel` won't work.

<table border="1" style="border-collapse: collapse;"><tbody><tr><td>ENV Variable Name</td><td>Default</td><td>Description</td></tr><tr><td><code>TINES_TUNNEL_SECRET</code></td><td></td><td>Base64 encoded JSON with tines tunnel secret</td></tr><tr><td><code>TUNNEL_ORIGIN_CERT</code></td><td><code>~/.cloudflared/cert.pem</code></td><td>Cloudflared certificate</td></tr><tr><td><code>TINES_TUNNEL_TO_COH_INGRESS_PORT</code></td><td>443</td><td><span>The port that Command Over HTTP is running on. By default it is 443 and that is where Tunnel will forward the request, but if you are using a customer <code>PORT</code> or <code>TLS_PORT</code>, you should set <code>TINES_TUNNEL_TO_COH_INGRESS_PORT</code> accordingly.</span></td></tr></tbody></table>

### Job management

## Introduction

An Action is run by a background worker in a job. Tines ensures [fair job orchestration](https://www.tines.com/blog/futureproofing-tines-fair-share-orchestration/) across all your stories. To view and manage background jobs in your tenant, click "Manage jobs" from the Admin page. **Note: **This option is only available for **Dedicated Tenants**.

## Workers active

To view the jobs currently being processed in your tenant, click "View" on the "WORKERS ACTIVE" card. The "In progress jobs" lists all active workers, the type of job, a description of the job, and how long the job has been running (i.e., how long the action is taking to run).

## Queued jobs

When all workers are busy, jobs will be placed on a queue until a worker is available to process them. If there is a large number of queued jobs, you may be experiencing a spike in events. If there are consistently a lot of queued jobs, consider [upgrading your plan ](https://www.tines.com/contact-support/)to add additional workers.

## Queue latency

This is the length of time it is currently taking for jobs to be processed by a worker after they have been added to the queue. If this time is consistently long, consider [upgrading your plan](https://www.tines.com/contact-support/) to add additional workers.

## Failed jobs

When a job fails, it will be retried 25 times over the course of approximately 21 days. After a job fails 25 times, it will be moved to the "Dead jobs" section and will not be retried again.

### Impersonation

Impersonation allows a tenant owner to grant access to Tines support to "impersonate" or log in to the tenant as if they are a specific user on that tenant.

## Enabling impersonation

If you're interacting with Tines support to work through an issue, they might suggest using impersonation to quickly attempt to directly reproduce, troubleshoot or fix the problem. If you agree, and would like to grant them permission to do so, you can navigate to a user in the user settings page and click to enable impersonation.

*Enabling Impersonation*

Once you enable impersonation of a user, it will automatically be disabled again 24 hours later. You may also manually disable it at any time.

When a Tines team member authenticates to log in to the tenant to begin impersonating a user, they do so via a dedicated, Tines managed identity provider, rather than using any SSO provider you may have configured.

Audit logs record when a Tines team member logs in to begin impersonation, and any action they take while impersonating.

## How we secure impersonation access

We have taken a number of steps to ensure that impersonation is handled securely and that you remain in control:

- Only tenant owners can enable impersonation.
- Once impersonation is enabled for a user, it is automatically disabled again after 24 hours.
- Impersonation of a user can also be manually disabled at any time.
- Disabling impersonation of a user will immediately terminate any active impersonation sessions for that user.
- Audit logs will record any actions take by Tines team members during impersonation.
- Authentication of Tines team members for the purposes of impersonation is restricted to select members of our support and engineering teams and requires the use of a managed Tines device.
- Each impersonation access request by a Tines team member is manually reviewed and approval to impersonate, if granted, expires after 2 hours.

### Story syncing

Some sophisticated deployments of Tines call for managing stories centrally, but having them run in a variety of different teams or even tenants, for example:

- [**MSSPs**](http://tines.com/mssps) often maintain consistent stories for end-customers, but each customer runs in their own team with segregated credentials, etc.
- **Multi-region deployments: **deploy centrally-managed stories to tenants in different regions, for compliance with data residency requirements.

Story syncing is the solution to problems like these.

> **NOTE:** Story syncing is available to MSSP and Enterprise customers. [Reach out to the team](https://www.tines.com/contact-support) to discuss adding the feature to your Tines tenant to explore further.

## Prerequisites

Story syncing can **only be managed by **[**tenant owners**](/docs/admin/user-administration/#permissions). The management UI can be found in the main settings sidebar, as ‘Story syncing’, within the ‘Access & security’ sidebar section.

In order to be used in story syncing, stories **must have **[**change control**](https://www.tines.com/docs/stories/change-control)** enabled**. When changes are published to the live version of the story, they will be automatically synced to all destinations.

## Creating sync destinations

When first accessing the story syncing UI in settings, a blank state will be shown. Hit ‘Get started’ to create your first sync destination.

![](https://www.datocms-assets.com/55802/1746895427-cleanshot-2025-05-10-at-12-42-46-2x.png)

In order to configure a sync destination, the following details must be provided:

- the team to sync **from**,
- the tenant and team to sync** to**,
- a Tines API key, if syncing to a remote tenant,
- and which stories should be synced.

![](https://www.datocms-assets.com/55802/1746797282-cleanshot-2025-05-09-at-09-24-47-2x.png)

Once you save your first sync destination, it will immediately be synced to, as well as on an ongoing bases as the selected stories are updated.

## Managing sync destinations

Once one or more sync destinations have been created, the dashboard will show them and their statuses – including surfacing any errors (e.g. when networking to a remote tenant) that are preventing sync from operating.

![](https://www.datocms-assets.com/55802/1746797417-cleanshot-2025-05-09-at-09-18-55-2x.png)

You can edit and delete existing sync destinations, and also run a manual sync at any time – handy for debugging problems such as networking errors.

![](https://www.datocms-assets.com/55802/1746800442-cleanshot-2025-05-09-at-10-04-33-2x.png)

## Detaching from sync

In certain circumstances, "detaching" a story from sync may be required. For example, if you no longer want the story to be centrally managed, but instead customized and locally managed in the destination team.

To achieve this, head to the story in the destination team. Then, in the story context menu in the top right (`**⋮**`), choose ‘Detach from sync’.

## Data segregation & security

**Only stories themselves are synced.** Objects such as credentials and resources are deliberately not synced – you'll need to set these up on the destination side. Think of objects like these as ‘[environment variables](https://en.wikipedia.org/wiki/Environment_variable)’ – they allow the centrally-managed story to run securely in its destination context, with the appropriate local secrets and context established.

## **Story syncing mechanics**

### Records

When syncing stories that use [records](https://www.tines.com/docs/records/), the record types are automatically created on the destination team if they don't exist. If a record type with the same name already exists on the destination, Tines will  update or create a new record type based on the schema changes:

Adding fields: If you add new fields to a record type on the source story and sync it, the existing record type on the destination will be updated with the new fields. This preserves any existing record data and history.

Removing or changing fields: If you remove fields or change field types (e.g., TEXT to NUMBER), a new record type with sequential naming will be created (e.g., "Alert" becomes "Alert (1)"). This prevents data loss and maintains backward compatibility.

> **TIP:** When using story syncing with records, plan your record type schema changes carefully. Adding fields is seamless, but removing or modifying fields will create new record types on destination teams.

## Licensing

Only the source/synced stories and [flows](https://explained.tines.com/en/articles/10860654-what-is-a-flow-in-tines) count towards your plan limits. You can sync to as many destinations as you like, without paying additional costs for those stories or flows.

## API

Sync destinations can additionally be entirely managed via our API. See [our `sync_destinations` API docs](https://www.tines.com/api/admin/sync_destinations/create/) for full details.

### Terraform

## Managing stories using terraform 

Tines supports Infrastructure as Code (IaC) for stories and other resources using the official [Terraform provider for Tines](https://github.com/tines/terraform-provider-tines/tree/main?tab=readme-ov-file#terraform-provider-tines). This enables you to manage, version, and automate your Tines stories alongside the rest of your infrastructure if desired.

### Things you might use this integration for 

- **Version control:** Store your stories as code in Git.
- **Automation:** Deploy and update stories automatically as part of your CI/CD pipeline.
- **Consistency:** Ensure your Tines environment matches your desired state.

**For more information see:**

- [Provider README & Usage](https://github.com/tines/terraform-provider-tines/blob/main/docs/index.md)
- [Examples](https://github.com/tines/terraform-provider-tines/tree/main/examples)

### Event limit alerts

> **NOTE:**
> Not seeing this feature? Talk to your tenant admin or [reach out to the Tines team](https://www.tines.com/contact-support/) to learn more.
> 
> Daily event limits **do not** apply to our [Community Edition](https://www.tines.com/get-started-with-tines-community-edition/) or [Starter Edition](https://www.tines.com/pricing/), both of which use monthly event limits. For more information, check out our edition comparison [here](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging#h_4dce8c5280).

## Introduction

Within the event limit alerts tenant owners can configure when and where notifications are sent when their tenant, stories, or teams are approaching and reaching daily event limits.

- **Tenant wide event limit** (limit configurable on self-hosted): The maximum number of events that can be created across all stories per day.
- **Global Story event limit **(limit configurable on self-hosted): The maximum number of events that can be created by a single story per day.
- **Team event limits** (limit configurable): The maximum number of events that can be created by a given team per day.

## Configuring Tenant and Story Notification Thresholds

1. Click on the gear icon next to the **Tenant wide event threshold alerts** or **Global Story event threshold alerts**.
2. Select the event usage percentages at which you would like to receive notifications.

![](https://www.datocms-assets.com/55802/1758903143-screenshot-2025-09-26-at-12-11-11-pm.png)

## Configuring Team Event Limits

1. Click on the gear icon next to the **Team event threshold alerts**.
2. Select a team from the dropdown.
3. Enter a daily limit for the team, or delete it to not apply a daily limit for the team. The daily limit for team cannot higher than the tenant event limit, and the tenant and story event limits will always apply even when the team does not have a daily limit.
4. Select the event usage percentages at which you would like to receive notifications.

![](https://www.datocms-assets.com/55802/1758903190-screenshot-2025-09-26-at-12-09-55-pm.png)

## Configuring Notifications

1. Click **Add recipients** or **Add a webhook** to add a new user or webhook recipient for notifications.
2. After adding a new user or webhook you can make updates to which types of notifications that user or webhook should receive. You can choose whether or not they should receive notifications for tenant and story event limits, as well as which teams they should receive notifications for.

![](https://www.datocms-assets.com/55802/1758903217-screenshot-2025-09-26-at-12-10-48-pm.png)

Event counts are cached in our system and their enforcement is therefore subject to some variation.

## Self-Hosted

### Before you begin

## Who to talk to

If you are considering deploying Tines in a self-hosted environment, please first contact your Account Executive or Customer Success Manager to explore your options.

Following that conversation you’ll be put in touch with the right technical resources in Tines to help you access the Tines Docker images, guide you through the installation, and provide ongoing support.

We highly recommend sharing the relevant pages in this section — particularly [Reference Architecture](https://www.tines.com/docs/reference-architecture/), [Connectivity Requirements](https://www.tines.com/docs/network-and-connectivity/), and [Sizing & Scaling](https://www.tines.com/docs/sizing-and-scaling/) — with your internal IT/infrastructure/network admin/security teams early (as appropriate) to provision the infrastructure, services, and network connectivity required.

## Compatible environments

Tines self-hosted artifacts are delivered as a set of Docker images, which means it runs in any containerized environment where Docker images are supported. Tines has been deployed successfully by our customers in the following environments:

- AWS Elastic Container Service (Fargate and EC2)
- AWS Elastic Kubernetes Service
- Azure Container Service
- Azure Kubernetes Service
- Google Kubernetes Engine
- Red Hat OpenShift
- VMware Virtual Machine
- Docker Compose on:
  
  - AWS EC2 instances
  - Azure Virtual Machine Instances
  - Google Virtual Machine Instances

Tines will support our application running on any container orchestration system and infrastructure once the requirements laid out in [Reference Architecture](https://www.tines.com/docs/reference-architecture/), [Connectivity Requirements](https://www.tines.com/docs/network-and-connectivity/), and [Sizing & Scaling](https://www.tines.com/docs/sizing-and-scaling/)  are satisfied (though correct operation of that container orchestration system and infrastructure remains the responsibility of the customer).

### Getting Started

Before deploying Tines in a self-hosted environment, complete the following prerequisites to ensure a smooth installation.

<table><tbody><tr><td><a href="/docs/self-hosted/getting-started/accessing-the-docker-registry"><strong>Accessing the Docker Registry</strong></a></td><td>Learn how to authenticate with the Tines container registry and pull the required images for your deployment.</td></tr><tr><td><a href="/docs/self-hosted/getting-started/verifying-tines-images"><strong>Verifying Tines Images</strong></a></td><td>Verify the integrity and authenticity of Tines container images before deploying to your environment.</td></tr><tr><td><a href="/docs/self-hosted/getting-started/helm-charts"><strong>Helm Charts</strong></a></td><td>Configure and deploy Tines using our official Helm charts.</td></tr></tbody></table>

#### Accessing the Docker Registry

> **NOTE:** Customers can access Tines images from our official Docker image registry at [registry.tines.com](https://registry.tines.com). To request access, please contact Tines Support. Once approved, Tines will provide an API key for the registry, and then you can follow the instructions below to authenticate.

## Logging In

After receiving your API key and tenant name, log in to the Tines registry using the following command (replace `API_KEY` and `tenant-name` with your values):

```bash
echo $API_KEY | docker login registry.tines.com -u tenant-name --password-stdin
```

## Available Images

The following images are available in the Tines registry:

- `tines/tines-ruby`
- `tines/tines-command-runner`
- `tines/tines-command-runner-fips`
- `tines/tines-nginx`
- `tines/tines-app`
- `tines/tines-app-with-fips`
- `tines/tines-tunnel`
- `tines/command-over-http`
- `redis`
- `postgres`

### Listing the Catalog

To list all available images in the registry:

```bash
#!/bin/bash
# Get the access token
TOKEN=$(curl --proto '=https' --tlsv1.2 \
  -H "Authorization: Basic $(echo -n '<username>:<api key>' | base64)" \
  -X GET "https://hq.tines.io/webhook/51005e85b2ab80feaee4a00133a80380/c7b65e314c887afe444d010c605e36a0" | jq -r .access_token)

# Use the token to query the catalog
curl -i --proto '=https' --tlsv1.2 \
  -H "Authorization: Bearer ${TOKEN}" \
  -X GET "https://registry.tines.com/v2/_catalog"
```

### Listing Tags for an Image

To list all tags for a specific image:

```bash
curl -L --proto '=https' --tlsv1.2 \
  -H "Authorization: Bearer ${TOKEN}" \
  "https://registry.tines.com/v2/tines/tines-app/tags/list"
```

### Looking Up an Image by Digest

To retrieve an image by its digest:

```bash
/code
curl -L --proto '=https' --tlsv1.2 \
  -H "Authorization: Bearer ${TOKEN}" \
  "https://registry.tines.com/v2/tines/tines-app/blobs/sha256:3d0e40e06d3325946c864441bb1192c9100401a0dc8e218c04a39a0a84ef97ae" | jq .
```

## 

Pulling Images

You can pull Tines images from the registry as follows:

```bash
docker pull registry.tines.com/tines/tines-ruby:vX.Y.Z
docker pull registry.tines.com/tines/tines-nginx:vX.Y.Z
docker pull registry.tines.com/tines/tines-app:vX.Y.Z
docker pull registry.tines.com/tines/tines-app-with-fips:vX.Y.Z
docker pull registry.tines.com/tines/tines-command-runner:vX.Y.Z
```

Images also support major/minor versions such as `vX` and `vX.Y` in addition to the full `vX.Y.Z`.

## ARM support

ARM-compatible images are available starting from version 37.2.0.

## Verifying Images

Tines images can be verified using [cosign](https://github.com/sigstore/cosign/releases/). Download and install cosign, then run:

```bash
cosign verify \
  --key ./cosign.pub \
  registry.tines.com/tines/tines-app:latest
```

Use the following public key for verification:

```bash
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUnTLRvsoHNEC04P2aBriS8p+0dAb
LgXrtHxEyC4R+rh44PB0unsYAEdNeLvPDnnn7GigylmaFwOPeQzLJigV6Q==
-----END PUBLIC KEY-----
```

#### Verifying Tines images

## Verifying images from Tines Registry

Tines images can be verified using [cosign](https://github.com/sigstore/cosign/releases/). Download and install cosign, then run (subbing in the name of the image):

```bash
cosign verify \
  --key ./cosign.pub \
  registry.tines.com/tines/tines-app:latest
```

Use the following public key stored in `cosign.pub` for verification:

```bash
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUnTLRvsoHNEC04P2aBriS8p+0dAb
LgXrtHxEyC4R+rh44PB0unsYAEdNeLvPDnnn7GigylmaFwOPeQzLJigV6Q==
-----END PUBLIC KEY-----
```

## Verifying images from Dockerhub

Tines images can be verified using [cosign](https://github.com/sigstore/cosign/releases/). Download and install cosign, then run the following (subbing in the name of the image) using the public key above:

```bash
cosign verify \
  --key ./cosign.pub \
  docker.io/tines/tines-app:latest
```



## Verifying images using Docker Content Trust (legacy)

Up until v33.0, we supported verifying by [Docker Content Trust.](https://docs.docker.com/engine/security/trust/#client-enforcement-with-docker-content-trust)

```bash
DOCKER_CONTENT_TRUST=1 docker pull tines/tines-app:latest
```

#### Helm Charts

# Deploying Tines with Helm

Tines can be deployed to Kubernetes using our official Helm chart. This guide covers everything required to configure, install, upgrade, and uninstall Tines using Helm.

We highly recommend sharing this page early with your internal IT/infrastructure/network admin teams to provision the infrastructure and services required before beginning the installation.

---

## Before you begin

The following must be in place before installing the Helm chart:

- A running Kubernetes cluster (AWS EKS, Azure AKS, Google GKE, or equivalent)
- [Helm v3](https://helm.sh/docs/intro/install/) installed and configured
- `kubectl` configured to communicate with your target cluster
- Access to the Tines image registry — see below
- A `StorageClass` in your cluster that supports `ReadWriteOnce` persistent volumes (the chart defaults to `gp3`)

---

## Tines image registry

Tines self-hosted artifacts are delivered as a set of Docker images hosted at `registry.tines.com`. Access to this registry is required before the Helm chart can be installed.

Please contact your Account Executive or Customer Success Manager to obtain your registry credentials. You will receive:

- Your **tenant name**, used as the registry username
- A **Tines Container Registry API key**, used as the registry password

Full details on available images and registry operations are available in the [Tines image registry documentation](https://www.tines.com/docs/self-hosting/tines-image-registry/).

### Verify registry access

Before proceeding, confirm your credentials are working:

```
echo $TINES_REGISTRY_KEY | docker login registry.tines.com \
  -u <your-tenant-name> \
  --password-stdin
```

#### 💡 Note

Customers who maintain a local registry mirror loaded with Tines-provided images may update the `deployments.*.image.registry` values in their `values.yaml` to point at their internal registry instead.

---

## Add the Helm repository

```
helm repo add tines https://helm.tines.com
helm repo update
```

To confirm the charts are available:

```
helm search repo tines
```

---

## Configure your values.yaml

Create a local `values.yaml` to override the chart defaults. The sections below cover the values that must be set before installation.

### Registry credentials

The chart creates a Kubernetes image pull secret from these values. They must be set or the chart will be unable to pull images from `registry.tines.com`.

```
tinesContainerRegistryCredentials:
  registry: registry.tines.com
  username: <your-tenant-name>
  password: <your-registry-api-key>
  email: <your-email>
```

### App secret token

This must be set to a random 128-character string. The chart will fail to render if this value is not provided. You can generate a suitable value by running:

```
openssl rand -hex 64
```

```
tinesApp:
  serverConfiguration:
    APP_SECRET_TOKEN: "<128-character-token>"
```

### Initial tenant configuration

These values are used to seed the first tenant and admin user on initial installation. They include the domain where your Tines instance will be accessible, and the credentials for the first user.

#### 💡 Note

Values in `initialTenantConfiguration` are only read on the first deployment. If you need to change these values after the initial installation, you will need to update the application or database directly.

```
tinesApp:
  initialTenantConfiguration:
    DOMAIN: tines.your-company.com
    SEED_EMAIL: admin@your-company.com
    SEED_EMAIL_PASSWORD: <password>
    SEED_FIRST_NAME: Admin
    SEED_LAST_NAME: User
    TENANT_NAME: your-company-name
```

### Database

Tines uses Postgres 14 `(>= 14.17)` as its persistent data store. By default, the chart deploys a Postgres container. We highly recommend using a dedicated hosted service (e.g. AWS RDS, Azure Database for PostgreSQL, Cloud SQL) where possible to simplify scaling and backups.

`DATABASE_USERNAME` and `DATABASE_PASSWORD` are required values — the chart will fail without them.

```
tinesApp:
  databaseConfiguration:
    DATABASE_HOST: db
    DATABASE_NAME: tines_production
    DATABASE_USERNAME: tines
    DATABASE_PASSWORD: <password>
    DATABASE_POOL: 20
    DATABASE_PORT: 5432
```

To use an external database instead of the bundled container, set `db.create: false` and point `DATABASE_HOST` at your managed endpoint:

```
db:
  create: false

tinesApp:
  databaseConfiguration:
    DATABASE_HOST: my-postgres.example.com
    DATABASE_NAME: tines_production
    DATABASE_USERNAME: tines
    DATABASE_PASSWORD: <password>
```

### Redis

Tines uses Redis 7.x as its cache, in-memory datastore, and queuing mechanism. By default, the chart deploys a Redis container. We highly recommend using a dedicated hosted service (e.g. AWS ElastiCache, Azure Cache for Redis) where possible to simplify scaling.

To use an external Redis instance, set `redis.create: false` and update `REDIS_URL`:

```
redis:
  create: false

tinesApp:
  redisConfiguration:
    REDIS_URL: redis://my-redis.example.com:6379/0
```

### SMTP server

Tines can be connected to a customer-provided SMTP server to send emails related to user management, monitoring, notifications, and to power the Send Email action. SMTP configuration is required to send the invitation email to the first user, unless `SEED_EMAIL_PASSWORD` is set, which bypasses the email invite flow.

```
tinesApp:
  emailConfiguration:
    SMTP_SERVER: smtp.your-company.com
    SMTP_DOMAIN: your-company.com
    SMTP_USER_NAME: smtp-user
    SMTP_PASSWORD: <password>
    # SMTP_PORT: "587"
    # SMTP_AUTHENTICATION: login
    # SMTP_ENABLE_STARTTLS_AUTO: true
```

### Persistent volumes

The chart provisions several persistent volume claims (PVCs). Your cluster must have a `StorageClass` that supports `ReadWriteOnce` access mode. The default values reference `gp3` — update `storageClassName` in your `values.yaml` to match what is available in your environment.

The following PVCs are created depending on your configuration:

| PVC | Component | Created when |
| --- | --- | --- |
| `tines-command-runner-data` | Command runner | Always |
| `pypi-server-data` | PyPI server | `tinesApp.tinesCommandRunner.pypiServerEnabled: true` (default) |
| `db-data` | PostgreSQL | `db.create: true` (default) |
| `redis-data` | Redis | `redis.create: true` (default) |

If you are using managed services for PostgreSQL and/or Redis, set `db.create: false` and/or `redis.create: false` respectively

#### 💡 Note

Persistent volume claims are not deleted when running `helm uninstall`. This is intentional. See the [Uninstalling](#uninstalling) section for steps to remove them manually if a full teardown is required.

### Load balancer

Customers may choose to either use the nginx container we provide or use dedicated load balancing infrastructure (e.g. AWS ALB, Azure Application Gateway, GCP Cloud Load Balancing) to route traffic to the web server component. It must support WebSockets to allow for real-time UI features like story run updates, new events on the storyboard, case updates, etc.

By default, the chart deploys the Tines nginx container and exposes it as a `NodePort` service on ports `30080` (HTTP) and `30443` (HTTPS). TLS can be terminated at nginx by providing a certificate and key directly.

Alternatively, the chart includes an optional Kubernetes Ingress resource that routes traffic directly to the web server on port 3000, bypassing the built-in nginx container. This is the recommended approach when using a cloud-native load balancer.

#### 💡 Note

When enabling the Ingress resource, we recommend setting `nginx.enabled: false` to avoid running both the built-in proxy and an external load balancer simultaneously.

#### Option A — Nginx with TLS termination

Provide your certificate and key directly in `values.yaml`:

```
nginx:
  enabled: true
  tinesCrt: |
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----
  tinesKey: |
    -----BEGIN PRIVATE KEY-----
    ...
    -----END PRIVATE KEY-----
```

#### Option B — External load balancer via Ingress

```
nginx:
  enabled: false

ingress:
  enabled: true
  className: nginx
  annotations:
    # --- AWS ALB ---
    # kubernetes.io/ingress.class: alb
    # alb.ingress.kubernetes.io/scheme: internet-facing
    # alb.ingress.kubernetes.io/target-type: ip
    # alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
    # alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:region:account:certificate/id

    # --- Azure Application Gateway ---
    # kubernetes.io/ingress.class: azure/application-gateway
    # appgw.ingress.kubernetes.io/ssl-redirect: "true"

    # --- GCP Cloud Load Balancing ---
    # kubernetes.io/ingress.class: gce
    # networking.gke.io/managed-certificates: my-cert
  hosts:
    - host: tines.your-company.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: tines-tls
      hosts:
        - tines.your-company.com
```

---

## Installing

Once your `values.yaml` is ready, create a dedicated namespace and install the chart:

```
kubectl create namespace tines

helm install tines-app tines/tines \
  -n tines \
  -f values.yaml
```

On first installation, the chart runs a one-time Kubernetes `Job` named `tines-app-setup` that runs database migrations, seeds the initial tenant configuration, and syncs integration templates. You can monitor it with:

```
kubectl get jobs -n tines
kubectl logs -n tines job/tines-app-setup
```

Once the setup job completes successfully, confirm all pods are running:

```
kubectl get pods -n tines
```

---

## Upgrading

To upgrade to a newer chart version or apply configuration changes, first pull the latest chart index and then run `helm upgrade`:

```
helm repo update

helm upgrade tines-app tines/tines \
  -n tines \
  -f values.yaml
```

To upgrade to a specific chart version:

```
helm upgrade tines-app tines/tines \
  -n tines \
  -f values.yaml \
  --version 38.4.1
```

#### 💡 Note

`helm upgrade` does not re-run `initialTenantConfiguration`. Those values are only applied on the first installation. All other configuration changes — SMTP, database settings, feature flags, etc. — are picked up on every deployment or pod restart.

If an upgrade needs to be rolled back:

```
helm rollback tines-app -n tines
```

---

## Uninstalling

To remove the Helm release and all associated Kubernetes resources:

```
helm uninstall tines-app -n tines
```

#### 💡 Note

Persistent volume claims are not removed by `helm uninstall`. If a full teardown is required, delete them manually after uninstalling:

```
kubectl delete pvc -n tines \
  db-data \
  redis-data \
  tines-command-runner-data \
  pypi-server-data
```

To remove the namespace entirely:

```
kubectl delete namespace tines
```

---

## Optional components

### Tines Tunnel

Tines Tunnel enables connectivity from your Tines Cloud instance to services running inside private networks. Please contact your Account Executive or Customer Success Manager to confirm this feature is included in your product license before enabling it.

To enable Tines Tunnel via the main chart:

```
tinesTunnel:
  create: true
```

Customers may also deploy Tines Tunnel as a standalone chart:

```
helm install tines-tunnel tines/tines-tunnel \
  -n tines \
  --set tunnelSecret=<your-tunnel-secret>
```

### Command-over-HTTP

Command-over-HTTP enables running commands on remote machines via an HTTP bridge. Please contact your Account Executive or Customer Success Manager to confirm this feature is included in your product license before enabling it.

To enable Command-over-HTTP via the main chart:

```
commandOverHttp:
  create: true
  tunnelSecret: <your-cohttp-secret>
```

Customers may also deploy Command-over-HTTP as a standalone chart, which additionally supports Kerberos and PowerShell configuration:

```
helm install tines-cohttp tines/tines-cohttp \
  -n tines \
  --set cohttpSecret=<your-cohttp-secret>
```

---

## Available charts

Tines Application

```
tines/tines
```

Command-over-HTTP

```
tines/tines-cohttp
```

Tines Tunnel

```
tines/tines-tunnel
```

---

## Further resources

- [Tines self-hosting documentation](https://www.tines.com/docs/self-hosting)
- [Tines image registry](https://www.tines.com/docs/self-hosting/tines-image-registry/)
- For support, contact [support@tines.com](mailto:support@tines.com)

#### Offline Documentation

*Available in v40.1 and later*

Self-hosted instances come with a built-in copy of the Tines product documentation. It's accessible without an internet connection and kept up to date automatically with each release.

### Accessing offline docs

You must be logged in to your Tines tenant to view offline docs.

There are two ways to access them:

- **From the menu:** Use the help dropdown in the navigation bar and select **Documentation**.
- **Via URL:** Navigate directly to `https://<your-tenant-domain>/docs`.

**

### Reference Architecture

#### System Overview

## System overview

There are 8 basic system components.

| Component | Runnable via Docker container | Runnable via dedicated infrastructure (provided by customer) | Purpose |
| --- | --- | --- | --- |
| **Load balancer** | ✅ | ✅ | Routing requests to the web server component(s). |
| **Web server** | ✅ |  | Handling requests from the UI, and from API and webhook clients. |
| **Background worker** | ✅ |  | Background execution of, e.g., workflows. |
| **Command runner** | ✅ |  | Execution of custom scripts (powering the “Run Script” action, “Automatic Transform” and the “Code Analyst” tool in Workbench). |
| **Postgres** | ✅ | ✅ | Persistent data store. |
| **Redis** | ✅ | ✅ | Caching of data from the database and queuing for the background worker(s). |
| **SMTP server (optional)** |  | ✅ | Sending emails for user management, notifications, monitoring and the “Send Email” action. |
| **LLM provider (optional)** |  | ✅ | Large language model API used in support of all AI features. |

![tines-architecture-overview-general](https://www.datocms-assets.com/55802/1778256386-connectivity-diagram.png)

The **Web server**, **Background worker** and **Command runner** components form the core Tines application and must be run as Docker containers.

The **Load balancer**, **Postgres** and **Redis** components may be run as Docker containers, but where possible we recommend using available managed/dedicated infrastructure.

The **SMTP server (optional)** and **LLM provider (optional)** must be provided by the customer.

### Load balancer

Customers may choose to either use the nginx container we provide or use dedicated load balancing or reverse proxying infrastructure (e.g. AWS ALB) to route traffic to the web server component.

If this is configured, all user, webhook and API traffic reaches the load balancer first. TLS is also terminated at the load balancer. If none of the requests to Tines are successful, the load balancer logs are a good place to start.

### Postgres

Tines uses Postgres 14 `(>= 14.17)` as its persistent data store. Customers may choose to either use [the official Postgres Docker image](https://hub.docker.com/_/postgres) and run it as a container or use dedicated infrastructure (e.g. AWS RDS) to host a Postgres database. We highly recommend using a hosted service where possible to simplify scaling and backups etc.

All persistent data is stored in Postgres, including stories, cases, users, records, audit logs, etc. Almost every request will hit Postgres, including the UI from the web server and action runs from the background workers. If your application is entirely down, Postgres is the best place to investigate.

### Redis

Tines uses Redis/Valkey 7.2 as its cache, in-memory datastore, and queuing mechanism. You can run either using [the official Redis Docker image](https://hub.docker.com/_/redis) and run it as a container or use dedicated hosted infrastructure (e.g. AWS ElastiCache) — we strongly recommend the latter to simplify scaling and operations.

Redis/Valkey handles user sessions, background job queues, and general application caching. If you're seeing issues with action run queues or slower-than-expected requests, this is a good place to start investigating.

### SMTP server (optional)

Tines can be connected to customer provided SMTP server to send emails related to user management, monitoring, notifications, and to power the Send Email action.

### LLM provider (optional)

As described in our [AI documentation on providers](https://www.tines.com/docs/admin/ai/#ai-providers), Tines can be connected to a number of different AI providers. Configuring this is required to enable all AI features.

### Identity provider (SSO) (optional)

Tines supports [SAML 2.0 and OIDC ](https://www.tines.com/docs/admin/single-sign-on/)for single sign-on. Most production deployments authenticate users via a customer-owned identity provider. Tines must be able to reach the identity provider over HTTPS.

#### Connectivity Requirements

| From host: | To host: | To port: | Notes: |
| --- | --- | --- | --- |
| *Any external webhook sources or API clients you wish to connect to Tines* | Load balancer | 80/443 | 
port 80 traffic redirected to 443

Must support WebSockets for real-time features in the UI

 |
| *The browsers of any users interacting with the Tines UI* | Load balancer | 80/443 | port 80 traffic redirected to 443 |
| Load balancer | Web server(s) | 3000 | port configurable |
| Web servers & Background workers | Postgres | 5432 | port configurable |
| Web servers & Background workers | Redis | 6379 | port configurable |
| Web servers & Background workers | Command runner | 4400 | port configurable |
| Web servers & Background workers | LLM provider | 443 | port configurable |
| Web servers & Background workers | SMTP server | 587 | port configurable |
| Web servers & Background workers | *Any APIs or IMAP servers you wish Tines to interact with* | Endpoint dependent (usually 443/993) |  |
| Web servers & Background workers | hq.tines.io | 443 | optional |
| Web servers & Background workers | integrations.tines.io | 443 | optional |
| Web servers & Background workers | story-library-data-2025-03.tines.com | 443 | 

optional

⚠️ DEPRECATED (v34.0.0+): This was deprecated in version 34.0.0

 |
| Tines Docker Image Registry | 

tines-docker-registry-bucket-81b969c8.s3.eu-west-1.amazonaws.com

 | 443 | 

Only used when pulling Images from registry.tines.com

 |
| Command runner | pypi.org | 443 | optional - this can be disabled (not recommended) or replaced with your own PyPi server (see [Deploying Tines](https://www.tines.com/docs/self-hosted/deploying-tines/) documentation for more) |
| Command runner | *Any external APIs or systems you wish Tines to interact with via custom scripts.* | Endpoint dependent |  |

#### Sizing & Scaling

##### Deployment tiers

Choosing the right deployment size starts with understanding your workload. This guide covers five reference tiers from single-node evaluation deployments through to high-availability Kubernetes clusters for production, using daily action runs as the primary scaling dimension.

All Tines self-hosted deployments consist of the same core components — **tines-app** (web server), **tines-sidekiq** (background worker), **tines-command-runner** (optional), **PostgreSQL**, **Redis**, and a **load balancer / reverse proxy** — scaled according to workload.

## Deployment Options and Sizing Overview

| Component | Tier 1: Small | Tier 2: Medium | Tier 3: Large | Tier 4: X-Large | Tier 5: XX-Large |
| --- | --- | --- | --- | --- | --- |
| **Daily Action Runs** | Up to 100K | Up to 1M | Up to 5M | Up to 10M | 25M+ |
| **Deployment Model** | Docker Compose | Docker Compose | Kubernetes (HA) | Kubernetes (HA) | Kubernetes (HA) |
| **Host / Cluster** | Single node | Single node + managed data tier | K8s cluster | K8s cluster | K8s cluster |
| **High Availability** | No | No | Yes | Yes | Yes |
| **Recommended For** | Evaluation, PoC, small teams | Small-to-mid production | Mid production | Large production | Enterprise / high-throughput |

> **NOTE:** These tiers are reference architectures. Actual resource requirements vary depending on story complexity, action payload sizes, webhook volume, concurrent users, and whether AI / command runner features are enabled. Start at the recommended baseline and scale up based on observed utilization.

## Tier 1: Small 

**Docker Compose (up to 100K actions/day)**

All containers run on a single host. Suitable for evaluation, proof-of-concept, and small production workloads.

### Host Specifications

| Resource | Specification |
| --- | --- |
| **vCPU** | 4 |
| **RAM** | 16 GB |
| **Disk** | 500 GB SSD |
| **OS** | Linux (Ubuntu 22.04+, RHEL 8+, Amazon Linux 2) |

### Container Layout (single host)

| Container | CPU Allocation | RAM Allocation | Instances |
| --- | --- | --- | --- |
| **tines-app** | shared | shared | 1 |
| **tines-sidekiq** | shared | shared | 1 |
| **tines-command-runner** | shared | shared | 1 |
| **tines-nginx** | shared | shared | 1 |
| **postgres** | shared | shared | 1 |
| **redis** | shared | shared | 1 |

### Notes

-   All services share the single host’s resources; no dedicated infrastructure required.
-   TLS terminated at the bundled nginx container.
-   Postgres and Redis run as local containers; regular backups via `pg_dump` cron job recommended.
-   **Not recommended** for production workloads requiring uptime SLAs.

## Tier 2: Medium

**Docker Compose (up to 1M actions/day)**

Single application host with externalized (managed) data services recommended. Suitable for small-to-mid production workloads.

### Application Host Specifications

| Resource | Specification |
| --- | --- |
| **vCPU** | 8 |
| **RAM** | 32 GB |
| **Disk** | 500 GB SSD |
| **OS** | Linux (Ubuntu 22.04+, Amazon Linux 2) |

### Container Layout (application host)

| Container | CPU Allocation | RAM Allocation | Instances |
| --- | --- | --- | --- |
| **tines-app** | shared | shared | 2 |
| **tines-sidekiq** | shared | shared | 2 |
| **tines-command-runner** | shared | shared | 2 |
| **tines-nginx** | shared | shared | 1 |

### Data Tier (managed services recommended)

| Service | Specification | Example (AWS) |
| --- | --- | --- |
| **PostgreSQL 14.x** | 4 vCPU, 32 GB RAM, 500 GB SSD, automated backups | RDS `db.r6g.xlarge` |
| **Redis 7.2** | 2 vCPU, 4 GB RAM | ElastiCache `cache.r6g.large` |

> If managed services are unavailable, Postgres and Redis may run as containers on the application host. In this case, increase the host to **16 vCPU / 64 GB RAM / 1 TB SSD** to accommodate the data tier locally.

### Recommended ENV Tuning

| Variable | Value | Notes |
| --- | --- | --- |
| `SIDEKIQ_CONCURRENCY` | 12 | Default; suitable for this tier |
| `RAILS_MAX_THREADS` | 16 | Default |
| `DATABASE_POOL` | 24 | Default |

### Notes

-   Externalizing Postgres and Redis to managed services simplifies backups, scaling, and maintenance.
-   TLS terminated at nginx container or an upstream load balancer.
-   Single point of failure at the application host — schedule maintenance windows accordingly.

## Tier 3: Large

**Kubernetes (up to 5M actions/day)**

Multi-pod Kubernetes deployment with HA data services. First tier to support horizontal pod autoscaling and data tier redundancy.

### Application Tier (K8s pods)

| Component | vCPU (per pod) | RAM (per pod) | Pod Count | Total vCPU | Total RAM |
| --- | --- | --- | --- | --- | --- |
| **tines-app** | 2 | 8 GB | 3 | 6 | 24 GB |
| **tines-sidekiq** | 2 | 8 GB | 5 | 10 | 40 GB |
| **tines-command-runner** | 2 | 8 GB | 2 | 4 | 16 GB |
| **Subtotal** |  |  | **10** | **20** | **80 GB** |

### Data Tier (High Availability)

| Service | vCPU (per instance) | RAM (per instance) | Instances | Total vCPU | Total RAM | Example (AWS) |
| --- | --- | --- | --- | --- | --- | --- |
| **PostgreSQL 14.x** | 8 | 64 GB | 2 (Primary + Secondary) | 16 | 128 GB | RDS `db.r6g.2xlarge` (Multi-AZ) |
| **Redis 7.2** | 2 | 4 GB | 2 (Primary + Replica) | 4 | 8 GB | ElastiCache `cache.r6g.large` (replica mode) |

### Cluster Sizing Guidance

| Resource | Recommendation |
| --- | --- |
| **Worker nodes** | 3+ nodes (spread across availability zones for resilience) |
| **Node size** | 8 vCPU / 32 GB each (minimum) |
| **Ingress** | Cloud load balancer with web application firewall (e.g., AWS ALB + WAF, GCP Cloud Armor) |
| **Storage** | Managed Postgres and Redis — no persistent volumes required for app pods |

### Notes

-   HPA (Horizontal Pod Autoscaler) can be enabled for tines-sidekiq and tines-app to handle burst workloads.
-   `DATABASE_READONLY_ENDPOINT` can be configured to offload read queries to the Postgres secondary.

> **NOTE:** For Tiers 3–5, Redis should be deployed in a **ReplicaSet** (primary + replica) configuration — not sharded. Tines does not provide or manage PostgreSQL high availability; customers are responsible for configuring their own Postgres replication and failover mechanisms (e.g., AWS RDS Multi-AZ, Aurora failover, Patroni, or equivalent).

## Tier 4: X-Large

**Kubernetes HA (up to 10M actions/day)**

High-availability Kubernetes deployment. All data services run in HA pairs. This is the recommended production configuration for large-scale deployments.

### Application Tier (K8s pods with HPA + ReplicaSets)

| Component | vCPU (per pod) | RAM (per pod) | Pod Count | Total vCPU | Total RAM |
| --- | --- | --- | --- | --- | --- |
| **tines-app** | 2 | 8 GB | 6 | 12 | 48 GB |
| **tines-sidekiq** | 2 | 8 GB | 9 | 18 | 72 GB |
| **tines-command-runner** | 2 | 8 GB | 3 | 6 | 24 GB |
| **Subtotal** |  |  | **18** | **36** | **144 GB** |

### Data Tier (High Availability)

| Service | vCPU (per instance) | RAM (per instance) | Instances | Total vCPU | Total RAM | Example (AWS) |
| --- | --- | --- | --- | --- | --- | --- |
| **PostgreSQL 14.x** | 16 | 128 GB | 2 (Primary + Secondary) | 32 | 256 GB | Aurora `db.r6g.4xlarge` |
| **Redis 7.2** | 2 | 4 GB | 2 (Primary + Secondary) | 4 | 8 GB | ElastiCache `cache.r6g.large` (cluster mode) |

### Cluster Sizing Guidance

| Resource | Recommendation |
| --- | --- |
| **Worker nodes** | 6+ nodes (across 3 availability zones) |
| **Node size** | 8 vCPU / 32 GB each (minimum) |
| **Ingress** | Cloud load balancer with web application firewall (e.g., AWS ALB + WAF, GCP Cloud Armor) |
| **HPA** | Enabled on all application deployments |

### Total Resource Summary

| Resource | Count |
| --- | --- |
| **Application pods** | 18 |
| **Application vCPU** | 36 |
| **Application RAM** | 144 GB |
| **Postgres vCPU** | 32 |
| **Postgres RAM** | 256 GB |
| **Redis vCPU** | 4 |
| **Redis RAM** | 8 GB |
| **Grand Total vCPU** | 72 |
| **Grand Total RAM** | 408 GB |

### Notes

-   HPA targets should be tuned based on CPU utilization (recommended: 60–70% target).
-   `DATABASE_READONLY_ENDPOINT` should be configured to leverage the Postgres secondary for read queries.

## Tier 5: XX-Large

Enterprise-scale high-availability deployment for the highest throughput requirements. Scales the Tier 4 reference architecture proportionally.

### Application Tier (K8s pods with HPA + ReplicaSets)

| Component | vCPU (per pod) | RAM (per pod) | Pod Count | Total vCPU | Total RAM |
| --- | --- | --- | --- | --- | --- |
| **tines-app** | 2 | 8 GB | 12 | 24 | 96 GB |
| **tines-sidekiq** | 2 | 8 GB | 18 | 36 | 144 GB |
| **tines-command-runner** | 2 | 8 GB | 6 | 12 | 48 GB |
| **Subtotal** |  |  | **36** | **72** | **288 GB** |

### Data Tier (High Availability)

| Service | vCPU (per instance) | RAM (per instance) | Instances | Total vCPU | Total RAM | Example (AWS) |
| --- | --- | --- | --- | --- | --- | --- |
| **PostgreSQL 14.x** | 32 | 256 GB | 2 (Primary + Secondary) | 64 | 512 GB | Aurora `db.r6g.8xlarge` |
| **Redis 6.x** | 4 | 16 GB | 2 (Primary + Secondary) | 8 | 32 GB | ElastiCache `cache.r6g.xlarge` (cluster mode) |

### Cluster Sizing Guidance

| Resource | Recommendation |
| --- | --- |
| **Worker nodes** | 12+ nodes (across 3 availability zones) |
| **Node size** | 8 vCPU / 32 GB each (minimum) |
| **Ingress** | Cloud load balancer with web application firewall (e.g., AWS ALB + WAF, GCP Cloud Armor) |
| **HPA** | Enabled and tuned on all application deployments |

### Total Resource Summary

| Resource | Count |
| --- | --- |
| **Application pods** | 36 |
| **Application vCPU** | 72 |
| **Application RAM** | 288 GB |
| **Postgres vCPU** | 64 |
| **Postgres RAM** | 512 GB |
| **Redis vCPU** | 8 |
| **Redis RAM** | 32 GB |
| **Grand Total vCPU** | 144 |
| **Grand Total RAM** | 832 GB |

### Notes

-   HPA targets should be tuned based on CPU utilization (recommended: 60–70% target).
-   `DATABASE_READONLY_ENDPOINT` should be configured to leverage the Postgres secondary for read queries.

##### Scaling Tines

The [deployment tier guides](https://www.tines.com/docs/deployment-tiers/) are a starting point, not a ceiling. As your workload grows, use OpenTelemetry tracing to make data-driven scaling decisions based on actual usage rather than estimates. Tines supports exporting OTEL traces from both tines-app and tines-sidekiq containers — see [Exporting OpenTelemetry Traces](/docs/self-hosted/configuring-tines/opentelemetry-traces/) for setup.

## Enabling Observability

Set the following ENV variables on **both** tines-app and tines-sidekiq containers:

| Variable | Value | Purpose |
| --- | --- | --- |
| `OTEL_ENABLED` | `true` | Enable trace export from Tines |
| `OTEL_AUTO_INSTRUMENTATION` | `true` | Enable auto-instrumented tracing (web requests, GraphQL, Sidekiq jobs, Postgres, Redis, external HTTP) |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Your collector endpoint | e.g., `http://otel-collector:4317` (gRPC) or `http://otel-collector:4318` (HTTP) |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `grpc`, `http/protobuf`, or `http/json` | Default: `grpc` |
| `OTEL_SERVICE_NAME` | Your identifier | Defaults to “Tines” if unset |

> **NOTE:** Auto instrumentation generates significantly more telemetry data. Sampling is recommended for production — see the OTEL Collector filter processor configuration in the [Tines docs](/docs/self-hosted/configuring-tines/opentelemetry-traces/) for recommended sampling rules.

## Key Metrics for Scaling Decisions

Before adjusting pod counts, first optimize `SIDEKIQ_CONCURRENCY` based on per-container CPU and memory limits. Once concurrency is tuned, use the following metrics to decide when to add or remove tines-sidekiq workers.

### When to Scale Up

| Metric | Condition | Action |
| --- | --- | --- |
| `action_run.default_queue_latency` | \> 1 second, CPU is low | Increase `SIDEKIQ_CONCURRENCY` |
| `action_run.default_queue_latency` | \> 1 second, CPU is high | Add more tines-sidekiq pods/containers |
| `percentage_of_workers_available` | < 20% | Add more tines-sidekiq pods/containers |

-   **`action_run.default_queue_latency`** measures how long jobs wait in the default Sidekiq queue before being picked up. A value of 0 means no jobs are waiting; sustained values above 1 second indicate the workers cannot keep up with the ingest rate.
-   **`percentage_of_workers_available`** tracks worker availability over time (requires auto instrumentation).

### When to Scale Down

| Metric | Condition | Action |
| --- | --- | --- |
| `action_run.default_queue_latency` | < 1 second (sustained) | Remove one tines-sidekiq pod/container |
| `percentage_of_workers_available` | \> 50% (sustained) | Remove tines-sidekiq pods/containers |

### Tracking Story-Level Performance

For deeper investigation into which stories or actions are driving load, use these trace attributes (requires auto instrumentation):

| Attribute | Purpose |
| --- | --- |
| `story_container.id` on `AgentReceiveJob` | Identifies which story an action run belongs to |
| `__trace.action_run_latency_ms` | Total action run latency |
| `__trace.action_run_time_to_start_ms` | Time from creation to execution start |
| `__trace.action_run_time_to_enqueued_ms` | Time from creation to queue entry |
| `scheduled.action_run_enqueue_job_v2.stories_with_pending_jobs` | Number of stories with pending work |
| `scheduled.action_run_enqueue_job_v2.pending_jobs_per_story` | Pending job count per story |

If only one story has a large backlog, the issue is likely story-specific (e.g., a slow external API call) rather than an infrastructure scaling problem.

### Deploying Tines

#### Docker Compose

##### Architecture Overview

## Architecture overview

![](https://www.datocms-assets.com/55802/1718634832-generic-tines-diagram-docker-compose-numbered.png)

## Basic architecture

Tines is a Rails application that uses a PostgreSQL database for its persistent data storage and Redis for its cached data storage and message queuing.

It features two core application containers: `tines-app` and `tines-sidekiq`. `tines-app` runs the Rails server for web application hosting/receiving webhooks. `tines-sidekiq` runs background worker processes. Both containers read and write to and from the PostgreSQL database and Redis cache - they don't communicate directly, instead, they swap data and messages through PostgreSQL/Redis.

Both the `tines-app` and `tines-sidekiq` containers use the same `tines-app` Docker image with different entry points (`start-tines-app` and `start-tines-sidekiq` respectively).

The `tines-app` and `tines-sidekiq` containers both make requests to external services to run your automation stories.

The `tines-sidekiq` container may be optionally configured to periodically load new Action templates from Tines (at [integrations.tines.com](http://integrations.tines.com/)) and to send basic version and feature usage information back to Tines (at hq.tines.io).

A `tines-nginx` container is usually deployed in front of the `tines-app` container to terminate SSL. Alternatively, any load balancer, load balancer service, or reverse proxy can fill this role.

## Serving the web application

A user's browser connects via HTTPS to the Tines web application via the `tines-nginx` container or an LB/reverse proxy, which terminates the SSL connection and connects to the `tines-app` container over HTTP.

## Running Actions on `tines-sidekiq`

The Tines product features 7 different types of automation "Actions", 3 of which cause Tines to connect to external services: IMAP, Email (SMTP), HTTP Request.

When a user "runs" an Action via the web application, a task is enqueued in the Redis cluster by the `tines-app` container. The `tines-sidekiq` container reads from this queue, reads related data from the PostgresSQL database and performs the Action, which may result in a HTTP, SMTP or IMAP request to an external server.

The data gathered from these requests is stored in an "event" in the PostgresSQL database.

Actions can also run as a result of an upstream Action running successfully, or on a schedule. When this happens, a task is enqueued in the Redis cluster by the `tines-sidekiq` container for each Action that will run.

## Testing Actions on `tines-app`

When "test" mode is invoked via the web application, the tines-app container directly executes the Action (rather than enqueuing it via the Redis instance for later execution by the `tines-sidekiq` container).

When this happens, the tines-app container may itself make a HTTP, SMTP or IMAP request to an external server.

## Receiving webhooks

An external service may post webhooks to the `tines-app` container through the same `tines-nginx` container or LB/reverse proxy used to host the web application.

When a webhook is received, the data is stored in an event in the PostgresSQL database. Further downstream Actions may be executed in turn by the `tines-app` container enqueuing tasks in the Redis cluster.

##### Prerequisites

### Preparation for Onboarding Meeting

To prepare for a standard Tines self-hosted deployment, you need to complete the following tasks **before your first onboarding meeting**:

- **Complete and submit the Self-Hosted questionnaire **
- Gather information for the configuration of the *.env configuration *file
- At the installation stage, you will be required to configure your .env file.
  
  - **Tip: **Before installation, use this[ **.env File Configuration Information Sheet **](/docs/self-hosted/deploying-tines/docker-compose/prerequisites/#env-file-configuration-information-sheet)to record this configuration information

> **NOTE:** For custom configuration and deployment, contact your Tines Sales Engineer (SE) or Customer Success Engineer (CSE).

### Deployment Requirements

1. Provision a Linux host machine (See [Deployment tiers](/docs/self-hosted/reference-architecture/sizing-and-scaling/deployment-tiers/) for sizing requirments)
2. Ensure that your host machine has access to the internet.
3. Open firewall inbound port 443 for HTTPs.
4. Install Docker and Docker Compose on your host machine.
5. Install netcat on your host machine.
6. Set up an SMTP Server for your Tines tenant.
7. Generate SSL/TLS certificate files and name the files tines.crt and tines.key.
8. To request the download installation package, open a Support ticket via [support@tines.com](mailto:support@tines.com).
9. Provision a Linux host machine for Tines Tunnel (if applicable).

### .env File Configuration Information Sheet

At the installation stage, you will be required to configure your .env file. 

**Prior to installation**, use this Information Sheet to collect the following information for the .env file:

```
Tenant name: 

Telemetry ID:

Seed email:

Seed first name:

Seed last name:

Domain:

Port:

SMTP domain:

SMTP user name:

SMTP password:

SMTP server:

SMTP port:

Email From address:
```

> **TIP:** If you do not need an SMTP configuration, the option needs to be commented out by adding #. If you leave these fields blank the installation will fail as it interprets this as a null value.

##### Deployment Guide

### Step 1. Install Dependencies

To install netcat, run the following:

**AWS Linux or RedHat/CentOS:**

```bash
sudo yum install nc docker
```

**Ubuntu or Debian:**

```bash
sudo apt-get install netcat docker.io
```

> **NOTE:**
> By default, Docker doesn't perform any log rotation. As a result, a significant amount of disk space can be used by logs generated by the Tines containers.
> 
> To switch to Docker's recommended logging driver, which rotates log files by default, we suggest setting the following value in **/etc/docker/daemon.json**, before restarting the Docker process (e.g. via **systemctl reload docker**):
> 
> {
>   "log-driver": "local"
> }
> 
> See [https://docs.docker.com/config/containers/logging/configure/](https://docs.docker.com/config/containers/logging/configure/) for more background and information about other logging drivers.

### Step 2. Install docker-compose

To install docker-compose, run the following:

```bash
# Replace {version} with the version of docker-compose you want to download
curl -L "https://github.com/docker/compose/releases/download/{version}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

chmod +x /usr/local/bin/docker-compose

ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
```

### Step 3. Create Installation Directory** **

Create a directory for the Tines installation, for example, /opt/tines:

```bash
sudo mkdir /opt/tines
```

### Step 4. Create tines.crt and tines.key Files

To create the tines.crt and tines.key files, run the following command within your installation directory:

```bash
sudo openssl req -x509 -out /opt/tines/tines.crt -keyout /opt/tines/tines.key -newkey rsa:2048 -nodes -sha256
```

#### Example

```bash
[ec2-user@ip-172-31-13-57 tines]$ sudo openssl req -x509 -out /opt/tines/tines.crt -keyout /opt/tines/tines.key -newkey rsa:2048 -nodes -sha256

Generating a 2048-bit RSA private key

..........................................+++

.....................................+++

writing new private key to '/opt/tines/tines.key'

-----

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank.

For some fields, there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:US

State or Province Name (full name) []:Massachusetts

Locality Name (eg, city) [Default City]:Boston

Organization Name (eg, company) [Default Company Ltd]:Tines

Organizational Unit Name (eg, section) []:Support

Common Name (eg, your name or your server's hostname) []:ec2-44-192-9-108.compute-1.amazonaws.com

Email Address []:yourname@yourcompany.com

[ec2-user@ip-172-31-13-57 tines]$ ls

commands.sh  docker-compose.yml  docs  postgres.tar  redis.tar  setup.sh  tines-app.tar  tines.crt  tines.key  tines-nginx.tar  upgrade.sh

[ec2-user@ip-172-31-13-57 tines]$ 
```

### Step 5. Get Access to the Installation Package[**​**](https://www.tines.com/docs/self-hosting/get-started/installation#downloading-install-package)** **

To access the installation package, follow these steps:

1. Send an email to support@tines.io and request that your cloud tenant be enabled to allow the download of the self-hosted installation package.
2. Once Tines Support has enabled the self-hosted installation package download, visit the /settings/upgrade page on your cloud tenant to copy the URL to the installation package. This URL will remain valid for 3 minutes. The file will be a zip archive named as follows: tines_<build id>.zip. The installation package contains the following files:

**Tines Self-Hosted Installation Package Files **

**Filename and Description**

- docker-compose.yml
  
  - Configuration file for Docker Compose
- .env.tmpl
  
  - Tines environment setting. Needs to be edited.
- postgres.tar
  
  - Postgres docker image
- redis.tar
  
  - Redis Docker image
- tines-app.tar
  
  - Tines application Docker image
- tines-nginx.tar
  
  - Tines nginx docker image
- upgrade.sh
  
  - Script to upgrade between Tines versions
- setup.sh
  
  - Script to install Tines

### Step 6. Download the Tines Application .zip File 

Use the URL that generated in the previous step to download the Tines application .zip file to your Linux host as shown below. Please retain the quotations around the URL as there are some special characters. 

```bash
sudo curl "https://tines-releases.s3.eu-west-1.amazonaws.com/tines_6a504e44_vxx_x_x.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA4AR6WFWBAAGLKFVE%2F20250616%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20250616T092200Z&X-Amz-Expires=180&X-Amz-SignedHeaders=host&X-Amz-Signature=aa2667feb2f23938e448ec4a0ff524f7f0ded8817867e7c5be29b1d37a6dedd5" --output /tmp/tines_6a504e44_vxx_x_x.zip
```

If you cannot download files remotely to your Linux host, you can use scp with your private key, as shown below:

```bash
C:\Users\Ben\Downloads>scp -i BenTest.pem tines_6a504e44_v31_4_0.zip ec2-user@ec2-44-192-9-108.compute-1.amazonaws.com:tines_6a504e44_v31_4_0.zip

The authenticity of host 'ec2-44-192-9-108.compute-1.amazonaws.com (44.192.9.108)' can't be established.

ECDSA key fingerprint is SHA256:4uSIhoRWK9PemgpfLh8l/gY0Ng/XFlr4t0x/aK4sSkY.

Are you sure you want to continue connecting (yes/no/[fingerprint])?

Please type 'yes', 'no' or the fingerprint:

Warning: Permanently added 'ec2-44-192-9-108.compute-1.amazonaws.com,44.192.9.108' (ECDSA) to the list of known hosts.

tines_6a504e44_v31_4_0.zip                                                            100%  368MB 2.9MB/s 02:08
```

### Step 7. Move Files to Installation Directory[**​**](https://www.tines.com/docs/self-hosting/get-started/installation#move-files-to-installation-directory)** **

Unzip the installation package and move all files to the installation directory:

```bash
unzip tines_<build_id>.zip

cp -rT tines_<build_id>/ /opt/tines
```

If you encounter an error like the following, then you need to re-visit the `/settings/upgrade` page on your tenant and regenerate the download URL. Please ensure that you begin your download within 3 minutes.

```bash
Archive:  /tmp/tines_6a504e44_v31_4_0.zip
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of /tmp/tines_6a504e44_v31_4_0.zip or
        /tmp/tines_6a504e44_v31_4_0.zip.zip, and cannot find /tmp/tines_6a504e44_v31_4_0.zip.ZIP, period.
```

### Step 8. Create a `.env` file using`**.env.tmpl**` 

The `.env` file contains Tines environment variables and is used to configure the Tines instance. 

To edit the `.env.tmpl` file (you can also find it at [Environment variable reference](https://www.tines.com/docs/environment-variable-reference/)), follow these steps: 

1. Open the `.env.tmpl` file using a text editor. 
2. Using the information that you recorded in the “.**env File Configuration Information Sheet**” step, update the following two sections of the .`env.tmpl` file to reflect your environment: Tenant Configuration and Email Configuration.
  
  **Tenant Configuration:** Enter basic information about the tenant, including its name, seed user details (the first user account that will be created) and domain (the FQDN).
  
  **Email Configuration: **Enter SMTP server details. Tines will use these when sending emails.

3. After updating the file, save it as `.env`.

### Step 9. Run Setup Script 

Your installation directory should now contain the following files:

- docker-compose.yml
- .env (updated and renamed)
- postgres.tar
- redis.tar
- tines-app.tar
- tines-nginx.tar
- tines.crt
- tines.key
- upgrade.sh
- setup.sh

Use the following command to run the Tines setup script:

```bash
bash setup.sh
```

Upon completion of the installation and the startup of the services and containers, a verification email will be sent to the email address that you specified in the SEED_EMAIL entry of the .env file. To verify the installation and to launch the Tines dashboard, click on the link provided in the email.

##### Run Script Setup

We offer the Run Python Script feature for self-hosted customers and for cloud customers using tunnels. For general details on how to use this, please refer to the documentation [here](https://www.tines.com/docs/actions/templates/run-python-script/). 

### Overview

The self-hosted configuration for this feature requires the configuration of a seperate application called `tines-command-runner`. Details can be found below on configuration.

Our Docker based Run Scripts implementation leverages two containers;

1. `tines-command-runner`
2. `pypi-server`([https://github.com/pypiserver/pypiserver)](https://github.com/pypiserver/pypiserver)

The `tines-command-runner` is where run scripts will execute.

Usage of `pypi-server` is **optional. **The `pypi-server` acts as a local python package index, making the installation of packages efficient and easy to configure as needed.  pypi-server can be overridden to use a custom index via environment variables to rely on a different package index. See environment variables [section](/docs/self-hosted/deploying-tines/docker-compose/run-script-setup/#environment-variables) for further details.

### Recommended System Requirements

> **INFO:**
> -   2 vCPU
> -   1.5GB Memory
> -   10-20GB disk space
> -   For heavier workloads you may need to scale up CPU and memory.
> -   CPU will improve the performance of compute-intensive tasks, memory will allow for more concurrent tasks to run.
> -   Disk space will need to be scaled based on the size and number of dependencies used, or any storage that your Run Scripts themselves make use of. Note that we pre-package a number of Python dependencies that you can leverage.
> -   A private network, since the container does not have a built-in authentication system and is meant to be reachable from a Tines tenant only.
> -   **Note that dependencies may be duplicated on disk across teams, so two different teams that use the same dependencies will still incur additional space for those dependencies.**

### Environment variables

You can override certain settings using environment variables.

The environment variables have defaults if not populated but can be tweaked if you want to run your own Python Package Index and point to it.

- `PIP_INDEX_URL` - Specifies the primary, default Python Package index.                                    Default: `https://pypi.org/simple`. Can be set to a custom index.
- `PIP_EXTRA_INDEX_URL` - Allows the customer to specify a fallback Python package index. Default: `pypi-server` , the provided local pypi server
- `NO_PIP_INDEX` - Disables any index, even if they are specified by `PIP_INDEX_URL` or `PIP_EXTRA_INDEX_URL`, instead relying entirely on the packages included already in the container.
- `UV_NATIVE_TLS` [optional] - When set (e.g. UV_NATIVE_TLS=1), uv uses the platform's native TLS certificate store. This is used to enable corporate proxies or custom CAs in the system store. When not set the variable is not used.

By default we specify the extra index as the pypi-server that runs alongside the command runner.

- `PIP_EXTRA_INDEX_URL`=http://pypi-server:8080/simple/
- `TRUSTED_HOST`=pypi-server
- `LOG_LEVEL` - Configures the logging level for the Python harness. Set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` (defaults to `INFO`). Set `LOG_LEVEL=DEBUG` to enable debug logging for troubleshooting. Logs are written to `/tmp/tcr-logs/harness-{environment_id}-{stdout,stderr}.log`.

For timeouts, you can configure:

- `RUN_SCRIPT_MAX_TIMEOUT` - This must be set on both tines-app and tines-sidekiq as well. see more information below.

### Pre-Downloaded Python Packages

The below common packages are pre-downloaded in the image for efficient access. These packages are available even if `NO_PIP_INDEX=true`. More packages may be added to this list in the future, versions may change as well.

```bash
annotated-types
anyio
beautifulsoup4
boto
boto3
click
cryptography
django
fastapi
flask
grpcio
grpcio-reflection
grpcio-tools
h11
idna
jupyter
lxml
matplotlib
networkx
nltk
numpy
openpyxl
pandas
paramiko
plotly
protobuf
pyarrow
pyopenssl
pydantic
pydantic_core
pytest
pytz
requests
scikit-learn
scipy
seaborn
setuptools
sniffio
sqlalchemy
starlette
statsmodels
sympy
typing_extensions
uvicorn
xlrd
```

**Note:** All transitive dependencies required by the above packages are also automatically downloaded and available for use. This means the actual number of available packages is larger than this explicit list, as each package brings in its own required dependencies during the build process.

### Privilege Escalation

The tines-command-runner image generates a unique Linux user for each Tines team using Run Script actions. To ensure each team's scripts are isolated from one another, tines-command-runner uses `sudo` to switch between these users at runtime.

If your environment restricts privilege escalation by default, you can explicitly grant only the minimum required Linux capabilities to the container.

 Required capabilities

- `SETUID`
  
  -  Allows the container to change the effective user ID (required for `sudo` to switch users)
- `SETGID`
  
  - Allows the container to change the effective group ID (required for `sudo` to switch groups)
- `AUDIT_WRITE`
  
  - Allows writing to the kernel audit log (required by PAM/sudo for security logging)

### Docker Compose Configuration

##### Step 1. Prepare the docker-compose.yml

Here is a docker-compose.yml setup example for tines-command-runner:

```yaml
services:
  tines-command-runner:
    image: tines/tines-command-runner:latest
    ports:
      - "4400:4400"
    depends_on:
      - pypi-server
    networks:
      - tines-net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:4400/health"]
      interval: 30s
      retries: 3
      start_period: 10s
      timeout: 10s
    restart: always

  pypi-server:
    image: pypiserver/pypiserver:latest
    hostname: pypi-server
    volumes:
      - python-packages:/data/packages
    expose:
      - "8080"
    networks:
      - tines-net
    command: run -P . -a . /data/packages
    restart: always

volumes:
  python-packages:

networks:
  tines-net:
```

**CPU & Memory for docker-compose:**

We recommend adding these based on your system's configuration to ensure that run scripts don't interfere with other containers on the host, for example, if you want to dedicate 2 of the host's CPU cores and 1GB of memory, try:

```yaml
services:
  tines-command-runner:
    mem_limit: 1g
    cpus: '2.0'
```

##### Step 2. Run the container via Docker compose

```bash
docker compose up -d
```

### Docker Configuration

##### Step 1. Run the container via Docker 

```bash
docker network create tines-net

docker run -d \
  --name tines-command-runner \
  -p 4400:4400 \
  --network tines-net \
  --health-cmd "curl -f http://localhost:4400/health" \
  --health-interval 30s \
  --health-retries 3 \
  --health-start-period 10s \
  --health-timeout 10s \
  --restart always \
  tines/tines-command-runner:latest

docker run -d \
  --name pypi-server \
  -v python-packages:/data/packages \
  --hostname pypi-server \
  --expose 8080 \
  --network tines-net \
  --restart always \
  pypiserver/pypiserver:latest run -P . -a . /data/packages
```

#### Important Considerations for increasing timeout

> **WARNING:**
> Increasing the timeout requires careful consideration.
> 
> #### 1\. Resource Requirements
> 
> Longer-running scripts need more resources. Scale your configuration accordingly. For example:
> 
> -   For a 300-second timeout, allocate at least 4GB memory and 4 CPU cores to the command runner
> -   Consider increasing the number of command runner containers based on your concurrent script execution needs
> -   For high concurrency, we recommend at least 2-3 command runner containers
> 
> #### 2\. System Impact
> 
> -   Monitor CPU and memory usage
> -   Consider impact on concurrent script executions
> -   Scale resources proportionally with timeout increases
> -   Set up resource utilization alerts

#### AWS Fargate

##### Architecture Overview

# Fargate architecture overview 

The below image outlines the high-level architecture of Tines when hosted with AWS Fargate. 

![](https://www.datocms-assets.com/55802/1750069164-fargatearchitecture.png)

*Tines Fargate architecture overview.*

## Explanation of steps 

- R1: The user's browser or a webhook initiates a request 
- R2: TLS Termination happens at a load balancer level
- R3: From the load balancer, the traffic is sent to the Tines web services,`tines-app`
- R4: `tines-app` which is a web service, communicates with PostgreSQL for storing and reading persistent data
- R5: `tines-app` then schedules background jobs to process any action runs in a Tines story. it enqueues the job details in Redis
- R6: `tines-sidekiq`, which is a background queue service, then picks up the jobs that are enqueued in Redis and performs further processing. tines-sidekiq then picks up jobs that are enqueued in Redis and performs further processing
- R7: `tines-sidekiq` communicates with PostgreSQL for storing and reading persistent data
- R8: `tines-app` and `tines-sidekiq` accordingly make requests to external APIs and endpoints for further processing in a story and actions

##### Deployment Guide

## Installation Steps

### Step 0. Before we start

We recommend running Tines in a dedicated [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html). You can follow [these instructions](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-example-private-subnets-nat.html) to create a new VPC with recommended configuration options. If you are running Tines in a VPC shared with other resources, you must ensure that there are two public and two private subnets that you can use for your Tines deployment.

There are some environment variables that are needed by multiple steps in this guide, so it's easiest to set them up in advance. 

We recommend storing all environment variables in a file like `tines-variables.env` . This way, you can re-use the environment variables even if you switch terminal shells. Once you create this file, you can add all the `export` lines mentioned in rest of this document into this file. Including the comments that start with `#` and then run `source tines-variables.env`. Each time you add a new line to this file, you will need to run `source tines-variables.env`, so that your terminal shell has loaded all the environment variables. 

Next, to set up these variables, you would first need access to the AWS CLI:

```bash
# Replace this with the name of AWS region you're running Tines in:
export AWS_REGION="eu-west-1"
# Replace this with the name of AWS account you're running Tines in:
export AWS_ACCOUNT_ID="123456789012"
# Replace this with the ID of the VPC you're running Tines in:
export VPC_ID="vpc-xxxxxxxx"
# Replace these with the IDs of two private subnets from different AZs in your VPC:
# Ensure there is an outbound route configured within the private subnets route table with a Nat Gateway or similar
export PRIVATE_SUBNET_ID_1="subnet-xxxxxxxx"
export PRIVATE_SUBNET_ID_2="subnet-xxxxxxxx"
# Replace these with the IDs of two public subnets from different AZs in your VPC:
export PUBLIC_SUBNET_ID_1="subnet-xxxxxxxx"
export PUBLIC_SUBNET_ID_2="subnet-xxxxxxxx"
# Replace this with the name of the latest Tines image:
export IMAGE="tines-app:latest"
export TCR_IMAGE="tines-command-runner:latest"
# Replace this with the name of the S3 bucket that will contain the environment files:
export ENV_FILE_S3_BUCKET="tines-test-env"
```

Before you begin, It's a good practice to execute each step individually. This makes it easier to debug by isolating any exceptions or errors that occur during specific steps.

### Step 1. Prepare an SSL certificate 

Following the instructions[ here](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html), create a certificate for the domain that you ultimately want your Tines instance to be accessible at in your browser.

For this step, it's easiest to just use the AWS console and follow the instructions for validation. The remaining steps will use the CLI.

ℹ️ Ensure to keep note of the `CERTIFICATE_ARN` from the cert created via AWS console, you will need it in a later step. 

### Step 2. Prepare the Tines Docker image 

For this step, you'll need access to the [tines-app ](https://hub.docker.com/repository/docker/tines/tines-app)Docker Hub repository. If you don't have access then the Tines support team can provide it.

To make things a little easier later on, we'll create an AWS ECR repository and copy the image from Docker Hub into that repository:

```bash
aws ecr create-repository --repository-name tines-app

export REGISTRY="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"

# First you must login with your personal docker account that has access to the tines repo login
docker login

# Make sure and update the below region if necessary
aws ecr get-login-password --region eu-west-1 | \
  docker login --username AWS --password-stdin $REGISTRY

docker pull tines/$IMAGE
docker pull tines/$TCR_IMAGE
docker tag tines/$IMAGE $REGISTRY/$IMAGE
docker tag tines/$TCR_IMAGE $REGISTRY/$TCR_IMAGE
docker push $REGISTRY/$IMAGE
docker push $REGISTRY/$TCR_IMAGE
```

### Step 3. Prepare some security groups 

To more clearly illustrate how the different components talk to each other, we'll create a security group for each one and open only the necessary ports. First we create the groups:

```bash
# Keep note of each GroupId value - we'll need them for later steps
aws ec2 create-security-group \
  --group-name tines-lb \
  --vpc-id $VPC_ID \
  --description "Load balancer security group for the Tines application"
aws ec2 create-security-group \
  --group-name tines-db \
  --vpc-id $VPC_ID \
  --description "Database security group for the Tines application"
aws ec2 create-security-group \
  --group-name tines-redis \
  --vpc-id $VPC_ID \
  --description "Redis security group for the Tines application"
aws ec2 create-security-group \
  --group-name tines-app \
  --vpc-id $VPC_ID \
  --description "tines-app container security group for the Tines application"
aws ec2 create-security-group \
  --group-name tines-sidekiq \
  --vpc-id $VPC_ID \
  --description "tines-sidekiq container security group for the Tines application"
aws ec2 create-security-group \
  --group-name tines-command-runner \
  --vpc-id $VPC_ID \
  --description "tines-command-runner container security group for the Tines application"
```

Then we assign their IDs to environment variables that we can use throughout the rest of the process:

```bash
# Replace this with the ID of the tines-lb security group:
export LOAD_BALANCER_SECURITY_GROUP_ID="sg-xxxxxxxxxxxxxxxxx"
# Replace this with the ID of the tines-db security group:
export DATABASE_SECURITY_GROUP_ID="sg-xxxxxxxxxxxxxxxxx"
# Replace this with the ID of the tines-redis security group:
export REDIS_SECURITY_GROUP_ID="sg-xxxxxxxxxxxxxxxxx"
# Replace this with the ID of the tines-app security group:
export APP_SECURITY_GROUP_ID="sg-xxxxxxxxxxxxxxxxx"
# Replace this with the ID of the tines-sidekiq security group:
export SIDEKIQ_SECURITY_GROUP_ID="sg-xxxxxxxxxxxxxxxxx"
# Replace this with the ID of the tines-command-runner security group:
export COMMAND_RUNNER_SECURITY_GROUP_ID="sg-xxxxxxxxxxxxxxxxx"
```

Then, we set up the necessary rules:

```bash
aws ec2 authorize-security-group-ingress \
  --group-id $LOAD_BALANCER_SECURITY_GROUP_ID \
  --protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress \
  --group-id $APP_SECURITY_GROUP_ID \
  --source-group $LOAD_BALANCER_SECURITY_GROUP_ID \
  --protocol tcp --port 3000
aws ec2 authorize-security-group-ingress \
  --group-id $DATABASE_SECURITY_GROUP_ID \
  --source-group $APP_SECURITY_GROUP_ID \
  --protocol tcp --port 5432
aws ec2 authorize-security-group-ingress \
  --group-id $DATABASE_SECURITY_GROUP_ID \
  --source-group $SIDEKIQ_SECURITY_GROUP_ID \
  --protocol tcp --port 5432
aws ec2 authorize-security-group-ingress \
  --group-id $REDIS_SECURITY_GROUP_ID \
  --source-group $APP_SECURITY_GROUP_ID \
  --protocol tcp --port 6379
aws ec2 authorize-security-group-ingress \
  --group-id $REDIS_SECURITY_GROUP_ID \
  --source-group $SIDEKIQ_SECURITY_GROUP_ID \
  --protocol tcp --port 6379
aws ec2 authorize-security-group-ingress \
  --group-id $COMMAND_RUNNER_SECURITY_GROUP_ID \
  --source-group $SIDEKIQ_SECURITY_GROUP_ID \
  --protocol tcp --port 4400
```

> **NOTE:** Running the above commands also populates the default outbound rule for each security group.

### Step 4. Create a Postgres database 

First, we create a database subnet group:

```bash
aws rds create-db-subnet-group \
  --db-subnet-group-name tines-db \
  --db-subnet-group-description "Tines database" \
  --subnet-ids $PRIVATE_SUBNET_ID_1 $PRIVATE_SUBNET_ID_2
```

Then, we create an Aurora PostgreSQL cluster with a single instance:

```bash
# Enter a password at the prompt after running this command.
# A value that contains punctuation other than underscores and dashes may cause errors.
echo "Type in the password you'd like to use for your database, then press (Enter): "
read -rs DB_PASSWORD

# Keep note of the Endpoint of the created object - we'll need it later
aws rds create-db-cluster \
  --db-cluster-identifier tines \
  --engine aurora-postgresql \
  --engine-version 14.6 \
  --backup-retention-period 7 \
  --master-username tines \
  --master-user-password $DB_PASSWORD \
  --database-name tines \
  --db-subnet-group-name tines-db \
  --vpc-security-group-ids $DATABASE_SECURITY_GROUP_ID

aws rds create-db-instance \
  --db-instance-identifier tines-db-1 \
  --engine aurora-postgresql \
  --db-instance-class db.r6g.xlarge \
  --db-cluster-identifier tines \
  --db-subnet-group-name tines-db
```

Later, additional instances can be added to the cluster to [support failover for higher availability](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraHighAvailability.html#Aurora.Managing.FaultTolerance). See documentation on how to configure this [here](/docs/self-hosted/best-practices/multi-az-aurora-cluster/).

If you need additional/field level encryption for the database, see documentation on how to enable this [here](https://www.tines.com/docs/self-hosting/additional-configuration/turning-on-database-field-level-encryption/).

### Step 5. Create a Redis cluster 

```bash
aws elasticache create-cache-subnet-group \
  --cache-subnet-group-name tines-redis \
  --cache-subnet-group-description "Tines Redis" \
  --subnet-ids $PRIVATE_SUBNET_ID_1 $PRIVATE_SUBNET_ID_2

aws elasticache create-cache-cluster \
    --cache-cluster-id "tines-redis" \
    --engine redis \
    --cache-node-type cache.t4g.small \
    --engine-version 6.x \
    --num-cache-nodes 1 \
    --cache-subnet-group-name tines-redis \
    --security-group-ids $REDIS_SECURITY_GROUP_ID
```

> **INFO:** While a **db.t4g.small** instance should be fine for many installations, you may see performance and reliability improvements from larger instance sizes if you run heavy workloads **db.r7g.large** for AWS Aurora and **cache.c7gn.large** for AWS Elasticache is often used for these cases.

### Step 6. Create a load balancer 

```bash
# Keep note of the ARN of the created object - we'll need it later in this step
aws elbv2 create-load-balancer \
  --name tines \
  --subnets $PUBLIC_SUBNET_ID_1 $PUBLIC_SUBNET_ID_2 \
  --security-groups $LOAD_BALANCER_SECURITY_GROUP_ID
```

ℹ️ Ensure to keep note of the `LOAD_BALANCER_ARN` returned from the above command, you will need it in a later step. 

Then create a target group:

```bash
# Keep note of the ARN of the created object - we'll need it later in this step
aws elbv2 create-target-group \
  --name tines-app \
  --protocol HTTP \
  --port 3000 \
  --target-type ip \
  --health-check-path '/is_up' \
  --vpc-id $VPC_ID
```

ℹ️ Ensure to keep note of the `TARGET_GROUP_ARN` returned from the above command, you will need it in a later step. 

Then create a listener:

```bash
# Replace this with the ARN of the load balancer created by the previous command:
export LOAD_BALANCER_ARN="arn:aws:elasticloadbalancing:eu-west-1:123456789012:loadbalancer/app/tines/ad446b3207c26fe7"

# Replace this with the ARN of the target group created by the previous command:
export TARGET_GROUP_ARN="arn:aws:elasticloadbalancing:eu-west-1:123456789012:targetgroup/tines-app/66559e249e21308d"

# Replace this with the ARN of a certificate that you created at step 1
export CERTIFICATE_ARN="arn:aws:acm:eu-west-1:123456789012:certificate/85b79526-e45f-4e76-8e3a-1d407142a62a"

aws elbv2 create-listener \
  --load-balancer-arn $LOAD_BALANCER_ARN  \
  --protocol HTTPS \
  --port 443 \
  --certificates CertificateArn=$CERTIFICATE_ARN \
  --ssl-policy ELBSecurityPolicy-2016-08 \
  --default-actions '[{"Type": "forward", "TargetGroupArn": "'$TARGET_GROUP_ARN'"}]'
```

### Step 7. Create a .env file

Make sure to make any necessary changes to the values in the `.env` file before moving on to step 8. In your `.env` file, you'll want to include the connection information from the Redis and Postgres clusters you created above, especially `DATABASE_HOST` and `REDIS_URL`. Our tines.env template is available [here](https://www.tines.com/docs/environment-variable-reference/).

```bash
# PostgreSQL database server connection configuration:
DATABASE_NAME=tines_production
# This is the number of concurrent connections to the database per container.
# For tines-app it should be at least equal to RAILS_MAX_THREADS (default 16).
# For tines-sidekiq it should be at least equal to twice SIDEKIQ_CONCURRENCY (default 8).
DATABASE_POOL=24
DATABASE_USERNAME=tines
# This password needs to match the value in your docker-compose.yml file.
# A value that contains punctuation other than underscores and dashes may cause errors.
# You can generate a value for this by running: openssl rand -hex 32
DATABASE_PASSWORD=__SET_YOUR_DATABASE_PASSWORD__
DATABASE_HOST=db
DATABASE_PORT=5432

# Set idle timeout before the connection should be flushed. Default 300 seconds
# DATABASE_IDLE_TIMEOUT=300

# Optional PotgreSQL SSL configuration
# PGSSLMODE=verify-full
# PGSSLCERT=client.crt
# PGSSLKEY=client.key
# PGSSLROOTCERT=ca.crt

# Optional setting for using a PostgreSQL schema other than "public".
# In this case you should also set the search path via an ENV var on on the DATABASE_USER's settings
# See https://www.tines.com/docs/self-hosting/additional-configuration/using-a-non-superuser-postgres-user/ for more details
# DATABASE_SCHEMA=public
# DATABASE_SCHEMA_SEARCH_PATH=public

# Redis connection configuration
# The full URL format is redis://<username>:<password>@<host>:<port>/<db_number>
# Use the rediss:// protocol instead for Redis over TLS
REDIS_URL=redis://redis:6379/1
```

### Step 8. Upload the .env file to S3

Both the `tines-app` and `tines-sidekiq` containers need to run with the exact same environment variables. To make this a bit easier, we have them both fetch the same .env file from S3.

```bash
aws s3api create-bucket \
  --bucket $ENV_FILE_S3_BUCKET \
  --region $AWS_REGION \
  --create-bucket-configuration "LocationConstraint=$AWS_REGION"

aws s3api put-public-access-block \
  --bucket $ENV_FILE_S3_BUCKET \
  --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

aws s3 cp tines.env s3://$ENV_FILE_S3_BUCKET/tines.env
```

### Step 9. Create the IAM roles for running the containers

```bash
# This only needs to be create once for an AWS account - if you're already using ECS, you can skip this command:
aws iam create-service-linked-role \
  --aws-service-name ecs.amazonaws.com

aws iam create-role \
  --role-name tinesTaskExecutionRole \
  --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [{ "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" }]}'

aws iam attach-role-policy \
  --role-name tinesTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

aws iam put-role-policy \
  --role-name tinesTaskExecutionRole \
  --policy-name TinesEnvAccess \
  --policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::'$ENV_FILE_S3_BUCKET'/tines.env"]}, { "Effect": "Allow", "Action": ["s3:GetBucketLocation"], "Resource": ["arn:aws:s3:::'$ENV_FILE_S3_BUCKET'"]}]}'
```

### Step 10. Create the ECS task definitions and cluster

```bash
aws logs create-log-group --log-group-name tines

export EXECUTION_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/tinesTaskExecutionRole"

aws ecs register-task-definition \
  --family "tines-app" \
  --memory 3072 \
  --network-mode awsvpc \
  --cpu 1024 \
  --execution-role-arn $EXECUTION_ROLE_ARN \
  --container-definitions '[{"name": "tines-app", "command": ["start-tines-app"], "image": "'$REGISTRY'/'$IMAGE'", "environmentFiles": [{"value": "arn:aws:s3:::'$ENV_FILE_S3_BUCKET'/tines.env", "type": "s3"}], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "tines", "awslogs-region": "'$AWS_REGION'", "awslogs-stream-prefix": "tines" }}, "portMappings": [{"containerPort": 3000}]}]'

aws ecs register-task-definition \
  --family "tines-sidekiq" \
  --memory 3072 \
  --network-mode awsvpc \
  --cpu 1024 \
  --execution-role-arn $EXECUTION_ROLE_ARN \
  --container-definitions '[{"name": "tines-sidekiq", "command": ["start-tines-sidekiq"], "image": "'$REGISTRY'/'$IMAGE'", "environmentFiles": [{"value": "arn:aws:s3:::'$ENV_FILE_S3_BUCKET'/tines.env", "type": "s3"}], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "tines", "awslogs-region": "'$AWS_REGION'", "awslogs-stream-prefix": "tines" }}}]'

aws ecs register-task-definition \
  --family "tines-command-runner" \
  --memory 2048 \
  --network-mode awsvpc \
  --cpu 2048 \
  --execution-role-arn $EXECUTION_ROLE_ARN \
  --container-definitions '[{
    "name": "tines-command-runner",
    "image": "'$REGISTRY'/'$TCR_IMAGE'",
    "environment": [
      {"name": "TINES_COMMAND_RUNNER_PORT", "value": "4400"},
      {"name": "TINES_COMMAND_RUNNER_ENV", "value": "production"}
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "tines",
        "awslogs-region": "'$AWS_REGION'",
        "awslogs-stream-prefix": "tines"
      }
    },
    "portMappings": [{"containerPort": 4400}],
    "linuxParameters": {
      "capabilities": {
        "add": ["SETUID", "SETGID", "AUDIT_WRITE"]
      }
    }
  }]'

aws ecs create-cluster --cluster-name tines
```

### Step 11. Seed the database

We run a one-off task, with a one-off command to prepare the database. This command will also trigger an invite email to the seed email address you specified in step 7, once the services in step 12 have been started. The invite link in this email is how you will sign into Tines for the first time.

```bash
aws ecs run-task \
  --cluster tines \
  --task-definition tines-app \
  --launch-type "FARGATE" \
  --network-configuration "awsvpcConfiguration={subnets=[$PRIVATE_SUBNET_ID_1,$PRIVATE_SUBNET_ID_2],securityGroups=[$SIDEKIQ_SECURITY_GROUP_ID],assignPublicIp=DISABLED}" \
  --overrides '{ "containerOverrides": [{ "name": "tines-app", "command": ["prepare-database"]}] }'
```

If you run into any issues during this initial setup, you can delete and recreate the database by running the following command. **Please note that this will delete all data in your Tines instance, so it should only be used if the initial setup fails.** After this command is run, you can repeat the command above to re-seed the database. If you have created the services from step 12 already, you should update them to set their *Desired tasks* to 0 while you run these commands.

```bash
# WARNING - this command will delete any data in the database.

aws ecs run-task \
  --cluster tines \
  --task-definition tines-app \
  --launch-type "FARGATE" \
  --network-configuration "awsvpcConfiguration={subnets=[$PRIVATE_SUBNET_ID_1,$PRIVATE_SUBNET_ID_2],securityGroups=[$SIDEKIQ_SECURITY_GROUP_ID],assignPublicIp=DISABLED}" \
  --overrides '{ "containerOverrides": [{ "name": "tines-app", "command": ["bundle", "exec", "rake", "db:drop", "db:create"], "environment": [{"name": "DISABLE_DATABASE_ENVIRONMENT_CHECK", "value": "1"}] }]}'
```

### Step 12. Start the services

```bash
aws ecs create-service \
  --cluster tines \
  --service-name tines-sidekiq \
  --task-definition tines-sidekiq \
  --desired-count 2 \
  --launch-type "FARGATE" \
  --network-configuration "awsvpcConfiguration={subnets=[$PRIVATE_SUBNET_ID_1,$PRIVATE_SUBNET_ID_2],securityGroups=[$SIDEKIQ_SECURITY_GROUP_ID],assignPublicIp=DISABLED}"

aws ecs create-service \
  --cluster tines \
  --service-name tines-app \
  --task-definition tines-app \
  --desired-count 2 \
  --launch-type "FARGATE" \
  --network-configuration "awsvpcConfiguration={subnets=[$PRIVATE_SUBNET_ID_1,$PRIVATE_SUBNET_ID_2],securityGroups=[$APP_SECURITY_GROUP_ID],assignPublicIp=DISABLED}" \
  --load-balancers "targetGroupArn=$TARGET_GROUP_ARN,containerName=tines-app,containerPort=3000"

aws ecs create-service \
  --cluster tines \
  --service-name tines-command-runner \
  --task-definition tines-command-runner \
  --desired-count 1 \
  --launch-type "FARGATE" \
  --network-configuration "awsvpcConfiguration={subnets=[$PRIVATE_SUBNET_ID_1,$PRIVATE_SUBNET_ID_2],securityGroups=[$COMMAND_RUNNER_SECURITY_GROUP_ID],assignPublicIp=DISABLED}"
```





> **INFO:** While one or two tasks in each service should be fine for many installations, you may see performance and reliability improvements from a higher if you run heavy workloads. The **tines-app** service should be scaled up to 5-8 tasks when there is a high amount of web requests coming into the installation, and the **tines-sidekiq** service should be scaled up to the same amounts when there are busy stories. If you scale these task counts up, you should also increase your RDS database instance class and Elasticache node instance class as well.

### Step 13. Set up a DNS record for your Load Balancer

Configure an `A` or a `CNAME` record pointing your domain (from Step 1) to your Load Balancer. You can find the Load Balancer's address using the following command:

```bash
aws elbv2 describe-load-balancers \
  --load-balancer-arns $LOAD_BALANCER_ARN \
  --query 'LoadBalancers[0].DNSName' \
  --output text
```

You can follow [these](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-elb-load-balancer.html) instructions if your domains DNS records are managed via Route53. Otherwise, you need to follow instructions specific to your DNS provider (e.g., [Cloudflare](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-zone-apex/),  [Google Cloud DNS](https://cloud.google.com/dns/docs/records), [Microsoft Azure DNS](https://learn.microsoft.com/en-us/azure/dns/dns-getstarted-portal)).

Once the `tines-app` and `tines-sidekiq` services are both up and running, it will send the email that was set up during step 11. You can then accept the invite to get started.

##### Run Script Setup

We offer the Run Script feature for self-hosted customers and for cloud customers using tunnels. For general details on how to use this, please refer to the documentation [here](https://www.tines.com/docs/actions/templates/run-python-script/). 

### Overview

The self-hosted configuration for this feature requires the configuration of a seperate application called `tines-command-runner`. 

Our Docker based Run Scripts implementation leverages two containers;

1. `tines-command-runner`
2. `pypi-server`([https://github.com/pypiserver/pypiserver)](https://github.com/pypiserver/pypiserver)

The `tines-command-runner` is where run scripts will execute.

Usage of `pypi-server` is **optional. **The `pypi-server` acts as a local python package index, making the installation of packages efficient and easy to configure as needed.  pypi-server can be overridden to use a custom index via environment variables to rely on a different package index. See environment variables [section](/docs/self-hosted/deploying-tines/aws-fargate/run-script-setup/#environment-variables) for further details.

### Environment variables

You can override certain settings using environment variables.

The environment variables have defaults if not populated but can be tweaked if you want to run your own Python Package Index and point to it.

- `PIP_INDEX_URL` - Specifies the primary, default Python Package index.                                    Default: `https://pypi.org/simple`. Can be set to a custom index.
- `PIP_EXTRA_INDEX_URL` - Allows the customer to specify a fallback Python package index. Default: `pypi-server` , the provided local pypi server
- `NO_PIP_INDEX` - Disables any index, even if they are specified by `PIP_INDEX_URL` or `PIP_EXTRA_INDEX_URL`, instead relying entirely on the packages included already in the container.
- `UV_NATIVE_TLS` [optional] - When set (e.g. UV_NATIVE_TLS=1), uv uses the platform's native TLS certificate store. This is used to enable corporate proxies or custom CAs in the system store. When not set the variable is not used.

By default we specify the extra index as the pypi-server that runs alongside the command runner.

- `PIP_EXTRA_INDEX_URL`=http://pypi-server:8080/simple/
- `TRUSTED_HOST`=pypi-server
- `LOG_LEVEL` - Configures the logging level for the Python harness. Set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` (defaults to `INFO`). Set `LOG_LEVEL=DEBUG` to enable debug logging for troubleshooting. Logs are written to `/tmp/tcr-logs/harness-{environment_id}-{stdout,stderr}.log`.

For timeouts, you can configure:

- `RUN_SCRIPT_MAX_TIMEOUT` - see more information below.

### Pre-Downloaded Python Packages

The below common packages are pre-downloaded in the image for efficient access. These packages are available even if `NO_PIP_INDEX=true`. More packages may be added to this list in the future, versions may change as well.

```bash
annotated-types
anyio
beautifulsoup4
boto
boto3
click
cryptography
django
fastapi
flask
grpcio
grpcio-reflection
grpcio-tools
h11
idna
jupyter
lxml
matplotlib
networkx
nltk
numpy
openpyxl
pandas
paramiko
plotly
protobuf
pyarrow
pyopenssl
pydantic
pydantic_core
pytest
pytz
requests
scikit-learn
scipy
seaborn
setuptools
sniffio
sqlalchemy
starlette
statsmodels
sympy
typing_extensions
uvicorn
xlrd
```

**Note:** All transitive dependencies required by the above packages are also automatically downloaded and available for use. This means the actual number of available packages is larger than this explicit list, as each package brings in its own required dependencies during the build process.

### Step 1. Prepare the tines-command-runner Docker image 

To make things a little easier, we mirror the image from Docker Hub into an ECR repository:

```bash
aws --profile test ecr create-repository --repository-name tines-command-runner

# Replace this with the address of the registry output in the previous command:
REGISTRY=306378194054.dkr.ecr.eu-west-1.amazonaws.com

aws ecr get-login-password --region eu-west-1 | \
  docker login --username AWS --password-stdin $REGISTRY

TCR_IMAGE=tines-command-runner:latest

docker pull tines/$TCR_IMAGE
docker tag tines/$TCR_IMAGE $REGISTRY/$TCR_IMAGE
docker push $REGISTRY/$TCR_IMAGE
```

### Step 2. Update the ECS task definitions 

To make it easy to access, we simply deploy the `tines-command-runner` container alongside the existing containers by adding it to the `tines-sidekiq` task. You can update the JSON payload in the AWS ECS UI by locating the latest task definition and include the following container definition:

```json
{
  "name": "tines-command-runner",
  "image": "$REGISTRY/tines-command-runner:latest",
  "environment": [
    {
      "name": "PORT",
      "value": "4400"
    }
  ],
  "logConfiguration": {
    "logDriver": "awslogs",
    "options": {
      "awslogs-group": "tines",
      "awslogs-region": "$AWS_REGION",
      "awslogs-stream-prefix": "tines"
    }
  },
  "portMappings": [
    {
      "containerPort": 4400
    }
  ]
}
```

### Step 3. Set a new environment variable

You also need to set the following environment variable on the Tines containers : `TINES_COMMAND_RUNNER_HOST=localhost`. This should be done on your [.env file](/docs/self-hosted/deploying-tines/aws-fargate/deployment-guide/#step-7-create-a-env-file) or task definition.

### Step 4. Restart the services 

```bash
aws ecs update-service --cluster <cluster_name> --service tines-sidekiq --force-new-deployment
```

### Run Script Timeout Configuration

The default timeout for  scripts is 60 seconds. For self-hosted deployments, you can override this by setting the `RUN_SCRIPT_MAX_TIMEOUT` environment variable on your [.env file](/docs/self-hosted/deploying-tines/aws-fargate/deployment-guide/#step-7-create-a-env-file)  or on the tines-sidekiq application.

This should be done on your [.env file](/docs/self-hosted/deploying-tines/aws-fargate/deployment-guide/#step-7-create-a-env-file) or task definition.

**.env file**

```bash
# Other tines-app env vars
RUN_SCRIPT_MAX_TIMEOUT=90
```

**AWS Fargate (add to tines-sidekiq task definition):**

```json
{
  "environment": [
    {
      "name": "RUN_SCRIPT_MAX_TIMEOUT",
      "value": "90"
    }
  ]
}
```

##### Storing ENV Variables in Secrets Manager

If you'd like to, you can provide environment variables to your `tines-app` and `tines-sidekiq` task definitions through AWS Secrets Manager. This is not essential, but can be done for improved security. 

First, [create a new secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) in AWS Secrets Manager for the environment variable using the name of the variable and its value as the key-value pair. Next, in ECS console, navigate to the task definition that will use the environment variable, and create a new revision. Under the `Environment variables` section, click `Add environment variable`. For consistency, use the same `Key` that you used for the secret. The `Value type` should be `ValueFrom` , and the `Value` is the `Secret ARN` from the Secrets Manager. 

You might also be interested in storing your database credentials in the Secrets Manager. See [here](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_database_secret.html) for information on how to do that.

### Configuring Tines

#### Environment variable reference

All self-hosted installations need a minimum set of environment variables to get started, and have additional (optional) configuration for further customization.

Below is a template `.env` file with a list of available configuration and default values. Depending on your installation type you may need to mantain this file in different locations (locally for Docker Compose installations, S3 or Secrets Manager for AWS deployments, or other configuration/secret administration tools.

```bash
###################################
# Required: Initial Tenant Configuration #
#
# Note: the values  in this section will only be read on the first deployment of
# the Tines instance. If you need to change these values after the first run, you will
# need to update the app or database directly. All other values in this file will be
# picked up by the Tines app on every deployment or server restart.

# Exception: TENANT_NAME and DOMAIN can be updated by changing the values here and 
# restarting the containers. The application will automatically detect changes to 
# these environment variables and update the tenant configuration accordingly.
###################################

# A human friendly identifier for this instance of Tines, e.g., "your-company-name"
# This can be updated by changing the value here and restarting the containers:
TENANT_NAME=

# This is the domain where your Tines instance will be accessible.
# This can be updated by changing the value here and restarting the containers:
DOMAIN=

# This will be the first user to be created and get invited to this Tines instance:
SEED_EMAIL=
SEED_FIRST_NAME=
SEED_LAST_NAME=

# If SEED_EMAIL_PASSWORD is set, this will bypass the email invite process for the first user and allow
# the SEED_EMAIL to login without SMTP configured using SEED_EMAIL:SEED_EMAIL_PASSWORD
# SEED_EMAIL_PASSWORD is *superseded* by either of the following 2 conditions:
# 1. If SMTP is configured correctly
# 2. If SSO is configured
SEED_EMAIL_PASSWORD=

# This address will be the default sender for all emails from this Tines instance:
# This can be configured on initial setup and changed subsequently at /admin/configuration
EMAIL_FROM_ADDRESS=

#############################
# Required: Server Configuration #
#############################

# Company name and stack name (e.g., example_prod). This is used to identify your tenant's telemetry data,
# if you have enabled that feature.
TELEMETRY_ID=

# This should match the port that you use to access the Tines UI.
# Unless you have chosen a custom port, you should use 443 as typical for HTTPS.
PORT=443

# This should be set to a random 128 character string to ensure security for your installation.
# Changing this value may force users to log in again.
# You can generate a value for this by running: `openssl rand -hex 64`
APP_SECRET_TOKEN=__SET_YOUR_SECRET_TOKEN__

#############################
# Required: Email Configuration #
#############################
# Outgoing email settings. This must be configured correctly in order for the invite email
# to be sent to the first user.
#
# To use Gmail or Google Apps, put your Google Apps domain or gmail.com
# as the SMTP_DOMAIN and your Gmail username and password as the SMTP_USER_NAME and SMTP_PASSWORD.
#
# If you have trouble with port 587 on Gmail, you can also try setting
# SMTP_AUTHENTICATION to login and the SMTP_PORT to 465.

SMTP_DOMAIN=
SMTP_USER_NAME=
SMTP_PASSWORD=
SMTP_SERVER=

# Optional SMTP settings
# Port. Default is 587.
SMTP_PORT=

# Authentication: `plain` (default), `login`, `cram_md5`, or `none`.
SMTP_AUTHENTICATION=login

# Detects if STARTTLS is enabled in your SMTP server and starts to use it. Defaults to false.
SMTP_ENABLE_STARTTLS_AUTO=true

# Use STARTTLS when connecting to your SMTP server and fail if unsupported. Defaults to false.
# SMTP_ENABLE_STARTTLS=

# Enables the SMTP connection to use SMTP/TLS (SMTPS: SMTP over direct TLS connection)
# SMTP_SSL=
# SMTP_TLS=

# When using TLS, you can set how OpenSSL checks the certificate.
# This is useful if you need to validate a self-signed and/or a wildcard certificate.
# Acceptable values: `none`, `peer`, `client_once`, `fail_if_no_peer_cert`
# SMTP_OPENSSL_VERIFY_MODE=

# Path to a file containing a PEM-format CA certificate that the SMTP server will use to verify your certificate.
# SMTP_OPENSSL_CA_PATH=
# Alternatively, inline contents of the certificate
# SMTP_OPENSSL_CA_FILE=

# Open timeout. Default: 30 seconds
# SMTP_OPEN_TIMEOUT=
# Read timeout. Default: 30 seconds
# SMTP_READ_TIMEOUT=

# Disable all email sending
# Uncomment this line to disable all email sending (including invite, monitoring, send email actions, etc.)
# DISABLE_EMAIL=true

############################
# Required: Database connections #
############################

# PostgreSQL database server connection configuration:
DATABASE_NAME=tines_production
# This is the number of concurrent connections to the database per container.
# For tines-app it should be at least equal to RAILS_MAX_THREADS (default 16).
# For tines-sidekiq it should be at least equal to twice SIDEKIQ_CONCURRENCY (default 12).
DATABASE_POOL=24
DATABASE_USERNAME=tines
# This password needs to match the value in your docker-compose.yml file.
# A value that contains punctuation other than underscores and dashes may cause errors.
# You can generate a value for this by running: openssl rand -hex 32
DATABASE_PASSWORD=__SET_YOUR_DATABASE_PASSWORD__
DATABASE_HOST=db
DATABASE_PORT=5432

# Set idle timeout before the connection should be flushed. Default 300 seconds
# DATABASE_IDLE_TIMEOUT=300

# Optional PotgreSQL SSL configuration
# PGSSLMODE=verify-full
# PGSSLCERT=client.crt
# PGSSLKEY=client.key
# PGSSLROOTCERT=ca.crt

# Optional setting for using a PostgreSQL schema other than "public".
# In this case you should also set the search path via an ENV var on on the DATABASE_USER's settings
# See https://www.tines.com/docs/self-hosting/additional-configuration/using-a-non-superuser-postgres-user/ for more details
# DATABASE_SCHEMA=public
# DATABASE_SCHEMA_SEARCH_PATH=public

# Redis connection configuration
# The full URL format is redis://<username>:<password>@<host>:<port>/<db_number>
# Use the rediss:// protocol instead for Redis over TLS
REDIS_URL=redis://redis:6379/1

########################
# Optional feature configuration #
########################

# Enables a periodic job to update public template date from integrations.tines.com.
SYNC_TEMPLATES=true

# Specify the default User-Agent header value for HTTP requests made
# by Agents that allow overriding the User-Agent header value.
# DEFAULT_HTTP_USER_AGENT=Tines (Advanced Security Automation; tines.com)

# Configure captcha feature for prompts
# PROMPT_CAPTCHA=false
# RECAPTCHA_SITE_KEY=
# RECAPTCHA_SECRET_KEY=

# Uncomment the line below to output Audit Logs to stdout
# AUDIT_LOGS_TO_STDOUT=true

# Audit log retention period (in days).
# When set, audit logs older than the specified number of days are automatically deleted.
# If not set audit logs are retained for 2 years.
# AUDIT_LOG_RETENTION_DAYS=90

# Log level for web server:
# Acceptable values: `debug`, `info` (default), `warn`, `error`, `fatal`, `unknown`
# Uncomment the line below to change the log level to warn, which will
# make the logs less verbose and only show warnings and errors instead of all activity.
# RAILS_LOG_LEVEL=warn

# When true, return development-style error pages (stack traces) in production. Only applies
# to self-hosted deployments. Default: false.
# RAILS_SHOW_DETAILED_EXCEPTION=true

# Setup Tines user on host machine
# SETUP_DEDICATED_LINUX_USER=true

# Set worker count per container
# SIDEKIQ_CONCURRENCY=12

# Seconds before a web request times out.
# Increase this if you experience frequent timeouts despite having appropriately scaled your infrastructure.
# RACK_SERVICE_TIMEOUT_SECONDS=35

# Size of each output payload (in bytes) generated for each event
# OUTPUT_PAYLOAD_SIZE_BYTES = 104857600 # 100MB

# Opt in to allow Tines to make certain database queries to a read-only endpoint to reduce main database load
# DATABASE_READONLY_ENDPOINT=

# Use proxy for external requests
# HTTP_PROXY=http://proxy.example.com:8080
# HTTPS_PROXY=http://proxy.example.com:8080

# Selectively don't use proxy for certain domains
# NO_PROXY=foo.bar.com,foobar.com

# Disable proxy redirect for nginx
# TINES_NGINX_PROXY_REDIRECT_OFF=true

# Disable ipv6 on nginx if using nginx and your network does not support ipv6
# DISABLE_NGINX_IPV6=true

# Generate HTTPS urls by default. Only applicable when `FORCE_SSL` is off.
# This can be useful if FORCE_SSL=false but you still want to generate HTTPS urls.
# USE_HTTPS=true

# Specify the port to be used in email links for the tenant's address (e.g., in invitation emails).
# TENANT_CLIENT_PORT=443

# Rack key space limit is the maximum number of bytes that can be used for form data keys.
# Default is 65536 bytes.
# RACK_KEY_SPACE_LIMIT=65536

# HTTP request payload size limit in bytes.
# Default is 83886080 (80MiB).
# PUMA_CONTENT_LENGTH_LIMIT=83886080

# Enable Content Security Policy
# CSP_ENABLED=true

# Cases file size limit in bytes.
# Default is 62914560 bytes (60MiB).
# FILE_SIZE_LIMIT=62914560

# Maximum number of pending events queued for a single Event Transformation
# action in delay mode. Once an action's queue reaches this size, new events
# routed to that action are dropped and an error is logged on the run.
# Set to 0 to disable the cap. Default is 20000.
# DELAYED_EVENT_QUEUE_SIZE_LIMIT=20000

# Maximum number of pending events queued for a single Event Transformation
# action in throttle mode. Once an action's queue reaches this size, new
# events routed to that action are dropped and an error is logged on the run.
# Default is 20000.
# THROTTLE_QUEUE_SIZE_LIMIT=20000

# The following are for enabling telemetry export from Tines
# Enable OpenTelemetry tracing export (required)
# OTEL_ENABLED=true
# Auto instrumentation can be set to true (optional)
# OTEL_AUTO_INSTRUMENTATION=false

# Recommended OTEL SDK configuration if OTEL_ENABLED=true above
# If the service name remains unset, we default to using "Tines"
# OTEL_SERVICE_NAME=Tines
# You may optionally set the following from the OTEL SDK 
# If unset, the defaults can be found in the OTEL SDK config docs
# OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" 
# OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"

########################
# Core configuration #
#
# These values should not be changed.
########################

# Ensure system logs are included in Docker container logs.
RAILS_LOG_TO_STDOUT=true

# Configure Rails environment. This should always be set to 'production'.
RAILS_ENV=production

# Force all requests to use SSL.
FORCE_SSL=true

```

#### Updating tenant limits

### Updating tenant limits

You can view your tenant's limits by checking your license at `/admin/license`. Please contact support if you notice an error or need a limit updated. If you don't have a license then you'll need to request one from support to proceed.

The license key will be in a format similar to the following:

![license key submission page](https://www.datocms-assets.com/55802/1750079221-screenshot-2025-06-16-at-14-05-57.png)



Once the license is submitted you will be able to view all features and limits associated with your Tines license. Any limit increase will require a new license which can be updated via the `Upload new license` button.

![license key after successful submission](https://www.datocms-assets.com/55802/1750079238-screenshot-2025-06-16-at-14-00-30.png)

Your tenant will have some default limits set for tenant and story daily events. Admin users can update these to a limit that suits your infrastructure by visiting `/admin/configuration` in the app, as seen below.

![form to update event limits for self-hosted tenant](https://www.datocms-assets.com/55802/1727711465-screenshot-2024-09-30-at-16-47-49.png)

#### AI provider configuration

You can connect your self-hosted Tines tenant to different AI providers.

If connecting to Anthropic or OpenAI (and other compatible schemas), the [configuration is the same as on cloud tenants](https://www.tines.com/docs/ai/).

# Amazon Bedrock

Configuring Amazon Bedrock is possible for tenants hosted on AWS infrastructure. Tines make requests to Bedrock using the assumed roles/credentials of the container hosting the Tines instance. Because of this, those credentials must be configured with the following IAM role permissions are required:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BedrockModelAccessPermissions",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel*",
        "bedrock:GetInferenceProfile",
        "bedrock:ListInferenceProfiles"
      ],
      "Resource": "*"
    }
  ]
}
```

> **INFO:** You can set more restrictive roles to only grant access to the specific models you choose.

We recommend enabling the latest Anthropic Claude models for best performance and capabilities.

You can now fully customize which AWS Bedrock models are enabled in Tines, allowing you to select any models available in your AWS region.



> **TIP:** Self\-Hosted Tines is designed to work with your AWS account and AWS Bedrock service, so your data never leaves your environment. Just sign up for AWS Bedrock and you're ready to go.

# Story copilot

In order to effectively use story copilot, it is strongly recommended to use the latest top AI model from the major foundational AI providers (Anthropic and OpenAI).

# Automatic mode

In order use automatic mode, Tines Command Runner must be configured and enabled.

#### LDAP authentication

The default Tines identity provider can be configured by self hosted customers to authenticate users via LDAP (as opposed to via email or SSO).

To enable it, configure the following environment variables:

## Required Settings

- `LDAP_HOST` - The hostname or IP address of your LDAP server
- `LDAP_BASE_DN` - The base distinguished name for searches (e.g., `dc=example,dc=com`)

## Optional Settings

- `LDAP_PORT` - The port number (default: 636)
- `LDAP_BIND_DN` - The distinguished name to bind with for searches (optional for anonymous bind)
- `LDAP_BIND_PASSWORD` - The password for the bind DN (required if LDAP_BIND_DN is set)

*ℹ️ ****Note****: *Use single quotes around any value that contains $, {, }, spaces, or other shell metacharacters (for example, LDAP_USER_FILTER='(sAMAccountName=${username})').

- `LDAP_USER_FILTER` - The filter to find users (default: `(uid=${username})`)
- `LDAP_EMAIL_ATTRIBUTE` - The attribute containing user email (default: `userPrincipalName`)
- `LDAP_FIRST_NAME_ATTRIBUTE` - The attribute containing first name (default: `givenName`)
- `LDAP_LAST_NAME_ATTRIBUTE` - The attribute containing last name (default: `sn`)
- `LDAP_ENCRYPTION` - Transport security: `simple_tls` (default), `start_tls`, or `none`
- `LDAP_CA_CERT_PEM` - Inline PEM string of one or more CA certificates
- `LDAP_CONNECT_TIMEOUT` - Connection timeout in seconds (default: 10)
- `LDAP_READ_TIMEOUT` - Read timeout in seconds (default: 10)

⚠️ **Security Warning**: Disabling SSL verification makes your LDAP connection vulnerable to man-in-the-middle attacks. Only disable these settings in secure, isolated environments for testing purposes.

- `LDAP_VERIFY_PEER` - Verify server certificate is valid and trusted (default: `true`)
- `LDAP_VERIFY_HOSTNAME` - Verify certificate matches the hostname (default: `true`)

# Example Configuration

### Basic Configuration

```bash
LDAP_HOST=ldap.example.com
LDAP_BASE_DN=dc=example,dc=com
LDAP_USER_FILTER='(uid=${username})'
```

### Active Directory Configuration

```bash
LDAP_HOST=ad.example.com
LDAP_PORT=389
LDAP_BASE_DN=dc=example,dc=com
LDAP_BIND_DN=cn=service-account,ou=service-accounts,dc=example,dc=com
LDAP_BIND_PASSWORD='your-service-account-password'
LDAP_USER_FILTER='(sAMAccountName=${username})'
LDAP_EMAIL_ATTRIBUTE=userPrincipalName
LDAP_FIRST_NAME_ATTRIBUTE=givenName
LDAP_LAST_NAME_ATTRIBUTE=sn
```

### SSL/TLS Configuration

By default we use LDAPS (simple TLS) with certificate and hostname verification.

LDAPS (recommended default):

```bash
LDAP_HOST=ldaps.example.com
LDAP_PORT=636
LDAP_ENCRYPTION=simple_tls
LDAP_BASE_DN=dc=example,dc=com
```

StartTLS on port 389:

```bash
LDAP_HOST=ldap.example.com
LDAP_PORT=389
LDAP_ENCRYPTION=start_tls
LDAP_BASE_DN=dc=example,dc=com
```

Provide custom CA certificate if necessary:

```bash
LDAP_CA_CERT_PEM="-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----"
```

More than one cert block is supported (for including a chain). You can use a command like the following to flatten a PEM file with a CA cert chain into a single line in a suitable format:

```bash
awk 'BEGIN{ORS="\\\\n"}{print}' certs/ca.crt | sed 's/\\\\n$//'
```

## How It Works

1. When LDAP is configured, an “Sign in with LDAP” option appears on the login page
2. Users enter their LDAP username and password
3. The system searches for the user in LDAP using the configured filter
4. If found, it attempts to authenticate by binding with the user’s credentials
5. Upon successful authentication, user information is extracted from the following LDAP attributes:
  
  - **Email** (required): Extracted from the attribute specified by `LDAP_EMAIL_ATTRIBUTE` (default: `userPrincipalName`)
  - **First Name** (optional): Extracted from the attribute specified by `LDAP_FIRST_NAME_ATTRIBUTE` (default: `givenName`)
  - **Last Name** (optional): Extracted from the attribute specified by `LDAP_LAST_NAME_ATTRIBUTE` (default: `sn`)
6. If the email address is missing or blank, authentication fails with “No email address found for user”
7. The user is then signed in to their Tines tenant

**Important**: The email attribute is required for successful authentication. If your LDAP directory uses a different attribute for email addresses (e.g., `mail` in Active Directory), ensure you set `LDAP_EMAIL_ATTRIBUTE` accordingly.

## Common Issues

1. **“Sign in with LDAP” not appearing** - Ensure LDAP_HOST and LDAP_BASE_DN are set
2. **“Incorrect username or password”** - Check the LDAP_USER_FILTER and LDAP_BASE_DN settings and verify the username and password are correct
3. **“Incorrect username or password” (when multiple users match)** - If your username filter returns multiple users, authentication will fail with the same error as an incorrect password. Check the server logs for “Multiple users found” messages:
  
  - Review your `LDAP_USER_FILTER` to make it more specific
  - Ensure usernames are unique in your LDAP directory
  - Consider using a more specific base DN to limit the search scope
4. **“LDAP connection failed”**
  
  - Verify `LDAP_ENCRYPTION` is set correctly (`simple_tls`, `start_tls` or `none`)
  - Ensure the server certificate is trusted via system trust or `LDAP_CA_CERT_PEM`
  - Confirm `LDAP_HOST`/`LDAP_PORT` and network connectivity
5. **Connection timeouts**
  
  - Increase `LDAP_CONNECT_TIMEOUT` for slow network connections
  - Increase `LDAP_READ_TIMEOUT` for slow LDAP servers or large directories
  - Check network connectivity and firewall rules
6. **SSL/TLS certificate errors**
  
  - **Certificate verification failed**: Set `LDAP_CA_CERT_PEM` with your CA certificate, or temporarily set `LDAP_VERIFY_PEER=false` for testing
  - **Hostname verification failed**: Certificate is valid but for different hostname - set `LDAP_VERIFY_HOSTNAME=false` if using load balancers or internal hostnames
  - **Self-signed certificate**: Set `LDAP_VERIFY_PEER=false` for testing, but consider using proper certificates in production

#### OpenTelemetry traces

You can now export OpenTelemetry traces on self-hosted Tines installations. This additional tracing data will provide you better insights on the performance of your Tines instance. To enable this capability, you will need to set the correct environment variables in both the tines-app and tines-sidekiq containers. For additional configuration you can utilize the environment variables provided by the [OTEL SDK](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/).

Tines utilizes the [OTLP Exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) to batch export spans to a user provided endpoint. This is the standard OpenTelemetry exporter that sends telemetry data using the OpenTelemetry Protocol.

- This solution supports both HTTP and gRPC transport protocols.
- The exporter as been configured with the BatchSpanProcessor for efficient export of spans.
- The exporter endpoint, as well as other optional settings, are configurable with environment variables provided by the OpenTelemetry SDK configuration.

### Environment Variables

The primary means of enabling and configuring the export of traces from Tines is by setting the following environment variables in your tines-app and tines-sidekiq containers.

**Required**

- OTEL_ENABLED=true This is the variable that we use to determine whether to build and export traces for self-hosted installations.

**Optional (but recommended)**

- [OTEL_EXPORTER_OTLP_PROTOCOL](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_protocol)=grpc, http/protobuf, or http/json The default is grpc.
- [OTEL_EXPORTER_OTLP_ENDPOINT](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/)=<where to receive traces> Typically http://localhost:4318 (HTTP) or http://localhost:4317 (gRPC) are the defaults, unless this environment variable is set to something else.
- [OTEL_SERVICE_NAME](https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_service_name)=<your company or service> The app will default this to “Tines” if none is provided.

Only the first variable mentioned, `OTEL_ENABLED`, is directly provided by Tines. The others are provided by the [OTEL SDK Configuration](https://opentelemetry.io/docs/languages/sdk-configuration/general/) and are recommended for successful export of OTEL traces in Tines.

### Optional: Auto Instrumentation

Tines can enable the following auto-instrumented tracing:

- **Web Requests**: All HTTP requests to your Tines instance
- **GraphQL Queries**: Complete GraphQL operation tracing with field-level paths
- **Background Jobs**: Sidekiq job execution and processing
- **Database Operations**: PostgreSQL queries (with sensitive data obfuscation)
- **External API Calls**: HTTP requests to external services
- **Redis Operations**: Cache and session operations

To enable auto instrumentation, set `OTEL_AUTO_INSTRUMENTATION=true.` Details on sampling auto instrumentation can be found below.

### Manage Performance with Tracing Data

When considering scaling your Tines instance up or down, we recommend tracking various trace fields. Before tuning the number of worker containers, be sure to select an ideal **SIDEKIQ_CONCURRENCY** value, which should be determined by container resource limitations (CPU/memory). Once that value has been determined, scaling workers can be done based on the following fields.

### Scaling Up

The first field to look at when investigating your Tines app’s performance is the **action_run.default_queue_latency.** This field is a Sidekiq queue performance metric that measures how long jobs have been waiting in the "default" queue before being processed. It tells us the time difference between when the oldest job in the queue was enqueued and the current time.

**Performance Indicators**:

- **Latency = 0**: No jobs waiting in queue
- **Latency > 0**: Jobs are waiting to be processed

**Recommendations:**

We consider high latency as anything over 1 second.

- If latency is high but CPU is low → Increase **SIDEKIQ_CONCURRENCY**
- If latency is high and CPU is high → Add more tines-sidekiq workers instead

The second field you can look at is **percentage_of_workers_available.** This metric can be used to track the availability of workers over time. This metric is only available with auto instrumentation enabled as its parent span is from the auto instrumentation.

**Recommendations**

- **percentage_of_workers_available** < 20% → Increase tines-sidekiq workers

### Scaling Down

The same attributes above can be used to determine whether to scale down.

**Recommendations**

- **action_run.default_queue_latency** < 1 second → Decrease by one tines-sidekiq workers
- **percentage_of_workers_available** > 50% → Decrease tines-sidekiq workers

### Tracking Story Performance

Most of the action run telemetry is only available when using the auto instrumentation. The **story_container.id** on **AgentReceiveJob** via the OpenTelemetry Sidekiq integration will allow you to determine which story an action comes from. Without auto instrumentation you can look at the **run_action** trace durations.

While the **percentage_of_workers_available** is a good indicator of infrastructure performance it may not provide insights on performance at the story and action level. For example, if only one story has pending jobs, it might be worth investigating the specific story to see if certain actions are running slowly.

- __trace.action_run_latency_ms
- __trace.action_run_time_to_start_ms
- __trace.action_run_time_to_enqueued_ms
- scheduled.action_run_enqueue_job_v2.stories_with_pending_jobs
- scheduled.action_run_enqueue_job_v2.pending_jobs_per_story

### Optional: Sampling Auto Instrumentation

We recommend sampling the auto instrumentation tracing. The auto instrumentation can provide far more data, but it also a lot more to manage. We have some recommendations for sampling rule within the [Otel Collector](https://opentelemetry.io/docs/collector/) if you plan to utilize auto instrumentation and the Otel Collector. Providing a filter processor will help reduce the amount of traces you’ll receive.

```
processors:
#Reference: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/filterprocessor/README.md
  filter: 
  error_mode: ignore # Recommended: ignore OTTL evaluation errors and continue processing 
    traces: 
    span: 
    # Drop PostgreSQL empty statements (just semicolon) - ALWAYS DROP 
    - 'attributes["db.system"] == "postgresql" and attributes["db.statement"] == ";"'
    
    # Sidekiq PostgreSQL Sampling (1 in 2000 = 0.05% retention)
    # Filters high-volume PostgreSQL operations from Sidekiq workers to reduce telemetry noise
    # Targets common database operations like EXECUTE, SELECT, INSERT, UPDATE, BEGIN, COMMIT, PREPARE
    - 'attributes["db.system"] == "postgresql" and resource.attributes["service.name"] == "sidekiq" and IsMatch(name, "^(tines|EXECUTE tines|SELECT tines|INSERT tines|UPDATE tines|BEGIN tines|COMMIT tines|PREPARE tines)$") and (end_time_unix_nano - start_time_unix_nano) < 100000000 and FNV(String(trace_id)) > -9214589400813948007'

    # Puma PostgreSQL Sampling (1 in 500 = 0.2% retention)
    # Filters high-volume PostgreSQL operations from Puma web servers with moderate sampling
    # Targets common database operations like EXECUTE, SELECT, INSERT, UPDATE, BEGIN, COMMIT, PREPARE
    - 'attributes["db.system"] == "postgresql" and resource.attributes["service.name"] == "puma" and IsMatch(name, "^(tines|EXECUTE tines|SELECT tines|INSERT tines|UPDATE tines|BEGIN tines|COMMIT tines|PREPARE tines)$") and (end_time_unix_nano - start_time_unix_nano) < 100000000 and FNV(String(trace_id)) > -9131194718863876096'

    # PostgreSQL Transaction Control Sampling (keep 1 in 5000 = 0.02% retention)
    # Filters transaction control statements that are high-volume but low-value for observability
    - 'attributes["db.system"] == "postgresql" and attributes["db.statement"] != nil and IsMatch(attributes["db.statement"], "^(BEGIN|COMMIT|ROLLBACK|SAVEPOINT|RELEASE)") and FNV(String(trace_id)) > -9219685678674763806'

    # PostgreSQL Prepared Statement Sampling (keep 1 in 5000 = 0.02% retention)
    # Targets prepared statement operations that generate high telemetry volume
    - 'attributes["db.system"] == "postgresql" and attributes["db.operation"] != nil and (attributes["db.operation"] == "EXECUTE" or attributes["db.operation"] == "PREPARE" or attributes["db.operation"] == "DEALLOCATE") and FNV(String(trace_id)) > -9219685678674763806'

    # Fast PostgreSQL Query Sampling (keep 1 in 50 = 2% retention)
    # Aggressive filtering for sub-100ms database operations (< 100 million nanoseconds)
    # Preserves slow queries (≥ 100ms) for performance monitoring
    - 'attributes["db.system"] == "postgresql" and attributes["db.operation"] != nil and (attributes["db.operation"] == "SELECT" or attributes["db.operation"] == "INSERT" or attributes["db.operation"] == "UPDATE" or attributes["db.operation"] == "DELETE") and (end_time_unix_nano - start_time_unix_nano) < 100000000 and FNV(String(trace_id)) > -8854558173021978624'

    # All Redis Operations Sampling (1 in 2000 = 0.05% retention)
    # Filters all Redis operations to reduce telemetry noise while preserving error patterns
    - 'attributes["db.system"] == "redis" and FNV(String(trace_id)) > -9214589400813948007'

    # Redis Pipeline Operations Sampling (1 in 5000 = 0.02% retention)
    # Filters high-volume Redis pipeline operations to reduce telemetry noise
    - 'attributes["db.system"] == "redis" and name == "PIPELINED" and FNV(String(trace_id)) > -9219685678674763806'

    # ActiveRecord ORM Sampling (keep 1 in 100 = 1% retention)
    # Reduces noise from Ruby on Rails ActiveRecord operations while preserving error patterns
    - 'IsMatch(name, "^ActiveRecord.*") and FNV(String(trace_id)) > -9131194718863876096'

    # (keep 1 in 5000 = 0.02% retention)
    # Filters out most health check that are localhost:3000 to reduce telemetry noise
    - 'attributes["http.target"] == "/is_up" and attributes["http.status_code"] == 200 and FNV(String(trace_id)) > -9219685678674763806'

  spanevent:
    # Sidekiq Auto-Generated Events - ALWAYS DROP
    # Drops all auto-generated Sidekiq instrumentation events that generate high telemetry volume
    - 'resource.attributes["process_type"] == "sidekiq" and IsMatch(name, "^(created_at|enqueued_at)$")'

```

#### Product telemetry

In order to better help us support our self-hosting customers, Tines installations send a limited amount of metadata about themselves back to us. This includes required fields such as the current version of the instance, your current license details and utilization, and some optional metadata such as teams, stories, active feature flags and extra debug fields. This data helps us understand your current license utilization, diagnose issues faster, and better understand how features are used.

To see the data being sent, or to opt in / out of sending any optional telemetry data, visit the `/admin/telemetry` path of your instance, e.g. `tines.company.com/admin/telemetry`. The data shown on that page is the exact data sent to Tines every hour.

Telemetry data will be sent to the domain `hq.tines.io` once every hour. You may need to allow requests to this domain in your network infrastructure to allow the data to be sent successfully.

We include your instance's domain in the metadata to allow us to identify which instance the data is coming from. If you want to provide a more specific identifier here, you can set the `TELEMETRY_ID` environment variable in your Tines instance.

#### SSL/TLS termination

This guide explains how to configure Tines to terminate SSL/TLS connections directly on the `tines-app` container, eliminating the need for a reverse proxy like Nginx.

## Prerequisites

Before enabling SSL termination on `tines-app`, ensure you have:

- SSL certificate file (`tines-app.crt`)
- Private key file (`tines-app.key`)
- Both files must be accessible to the `tines-app` container

## Quick Start

### 1 - Generate or obtain SSL certificates

Place your SSL certificate and private key in the application directory:

```bash
# Self-signed certificate (for testing only)
openssl req -x509 -newkey rsa:4096 -keyout tines-app.key \
  -out tines-app.crt -days 365 -nodes \
  -subj "/CN=your-domain.com"

# Production: Use certificates from your certificate authority
cp /path/to/your/certificate.crt tines-app.crt
cp /path/to/your/private-key.key tines-app.key
```

### 2 - Set file permissions

```bash
chmod 644 tines-app.key
chmod 644 tines-app.crt
```

### 3 - Provide files to container

The following files are checked at startup:

- **Certificate**: `/home/tines/tines/tines-app.crt`
- **Private Key**: `/home/tines/tines/tines-app.key`

If both files exist, SSL will be enabled automatically. In a Docker Compose installation, you can provide them via Docker volumes. Here is a sample `docker-compose.yml` service override for `tines-app`:

```yaml
services:
  tines-app:
    image: tines/tines-app:latest
    # ...other config...
    volumes:
      - ./tines-app.crt:/home/tines/tines/tines-app.crt:ro
      - ./tines-app.key:/home/tines/tines/tines-app.key:ro
    environment:
      - TINES_WEB_SSL_PORT=3001 # Set to desired HTTPS port
      # - TINES_WEB_SSL_CIPHERSUITES=... (optional)
      # - TINES_WEB_SSL_CIPHER_FILTER=... (optional)
```

Make sure the paths and permissions for your `.crt` and `.key` files are correct.

### 4 - Start Tines

The `tines-app` container will automatically detect the certificate files and enable SSL. The HTTPS server will be available on port 3001 by default.

## ENV Configuration

- `TINES_WEB_SSL_PORT` - Port for HTTPS connections. Default when not provided: `3001`
- `TINES_WEB_SSL_CIPHERSUITES` - TLS 1.3 cipher suites (colon-separated)
- `TINES_WEB_SSL_CIPHER_FILTER` - TLS 1.2 and earlier ciphers (colon-separated)

### Disabling Weak Ciphers

To explicitly exclude weak or compromised ciphers, use the `!` operator:

```bash
# Exclude all CBC mode ciphers
TINES_WEB_SSL_CIPHER_FILTER="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:!CBC"

# Exclude RC4, MD5, and export ciphers (with an explicit allowlist)
TINES_WEB_SSL_CIPHER_FILTER="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:!RC4:!MD5:!EXPORT"

# Exclude CBC, RC4, and MD5 from the OpenSSL default set:
TINES_WEB_SSL_CIPHER_FILTER="DEFAULT:!CBC:!RC4:!MD5"
```

## Testing Your Configuration

#### Test SSL/TLS Connection

```bash
openssl s_client -connect your-server:3001 -tls1_3
openssl s_client -connect your-server:3001 -tls1_2
```

#### Scan Available Ciphers

Using `nmap`:

```bash
nmap --script ssl-enum-ciphers -p 3001 your-server
```

#### Database field-level encryption

[Tines Credentials](https://www.tines.com/docs/credentials/) are stored in the database provisioned for your self-hosted Tines tenant. While we recommend ensuring that the PostgreSQL database has encryption at rest and in transit, you can also leverage and ensure encryption at a field level. This ensures that Tines Credentials are persisted with `AES-256` encryption using the keys that you provide and are not stored as plain text.

## Setup

To opt-in your self-hosted installation to take advantage of this feature, you can follow the steps below:

- Add the following environment variables to your setup. This is usually the `.env` file if you are on a Docker Compose setup.

```
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=ABC123
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=ABC123
```

- Replace `ABC123` with unique keys that are 32 bytes in length.
- Perform a rolling restart of your containers. After this, any new Tines Credential you create will be encrypted at a field level.
- To encrypt existing records, run the following from a `tines-app` or `tines-sidekiq` container:

```bash
bundle exec rake tines:encrypt_models
```

And that's all you need to do.

> **WARNING:** Once these values are set in the .env file, they cannot be changed. To rotate keys, see the instructions in the next section.

## Rotating Keys

In case you need to rotate the primary key, you can follow the steps below:

- Introduce the new key as a new environment variable:

```
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_NEW=ABC123

# Keep the following as is
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=ABC123
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=ABC123
```

- Perform a rolling restart of your containers. After this, any new Tines Credential you create will be encrypted at a field level using the new key, and decryption will be attempted using both the existing and new keys.
- Finally, to re-encrypt existing records, run the following from a `tines-app` or `tines-sidekiq` container:

```bash
bundle exec rake tines:encrypt_models
```

- Once done, you can now replace the contents of `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY` with `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_NEW `and drop `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY_NEW` altogether from your environment store.



And that's all you need to do to rotate the primary key.

#### Using Redis Sentinel

Redis Sentinel handles automatic failover of Redis masters and service discovery. Tines can now use Redis Sentinel. It is currently available in the `v13.3.4` version (experimental). 

**Please note**, we only recommend usage of Redis Sentinel when usage of services like AWS Elasticache or similar isn't an option.

In order for your Tines installation to work with Redis Sentinel, you will need to setup the following environment variables:

**Required**

- `REDIS_SENTINEL_ENDPOINTS`: A comma separated list of sentinel endpoints with port:
  
  - Example: `export REDIS_SENTINEL_ENDPOINTS="redis://redis-sentinel:26379,redis://redis-sentinel:26380"`
- `REDIS_SENTINEL_MASTER_NAME` 
  
  - Default `master`

**Optional**

For TLS and authentication purposes:

- `REDIS_SENTINEL_PASSWORD`
- `REDIS_SENTINEL_USE_SSL`
  
  - This should be set to true in order to use TLS/SSL based connection with Redis sentinels.
  - Example: `export REDIS_SENTINEL_USE_SSL=true`
- `REDIS_SENTINEL_CA_FILE_PATH`
  
  - Location of the respective file (only used if `REDIS_SENTINEL_USE_SSL` is true)
- `REDIS_SENTINEL_CERT_FILE_PATH`
  
  - Location of the respective file (only used if `REDIS_SENTINEL_USE_SSL` is true)
- `REDIS_SENTINEL_KEY_FILE` 
  
  - Location of the respective file (only used if `REDIS_SENTINEL_USE_SSL` is true)



**Note** : Not providing `REDIS_SENTINEL_ENDPOINTS` will default the application to use `REDIS_URL` as the base URL for connecting to standard Redis.

#### Non-Superuser postgres user

By default, Tines expects the `DATABASE_USERNAME` to be a superuser so it can create the database, enable extensions, and apply user-level session configuration during setup and upgrades.

If your environment requires a least-privilege approach, you can use a non-superuser — but you'll need to handle a few things manually before starting Tines.

> **Warning:** All steps in this guide must be completed by a Postgres superuser **before** starting Tines for the first time. Skipping any step will cause Tines to fail on startup.

---

## Prerequisites

Before starting Tines with a non-superuser, a superuser must complete the following steps.

### 1. Create the database and user

Replace `tines_user`, `<password-here>`, and `tines_db` with the values you intend to set for `DATABASE_USERNAME`, `DATABASE_PASSWORD`, and `DATABASE_NAME` respectively.

```sql
-- Create the Tines user. Values should match DATABASE_USERNAME and DATABASE_PASSWORD.
CREATE USER tines_user WITH PASSWORD '<password-here>';

-- Create the database. Value should match DATABASE_NAME.
CREATE DATABASE tines_db;

-- Make the user the owner so they have full privileges on the database.
ALTER DATABASE tines_db OWNER TO tines_user;
```

> **Note:** The `DATABASE_USER` must have permission to create, use, and drop objects (tables, sequences, indexes) within the database. Making them the database owner is the simplest way to guarantee this.

### 2. Enable required extensions

Tines requires the following Postgres extensions to be enabled before running for the first time.

```sql
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS plpgsql;
CREATE EXTENSION IF NOT EXISTS btree_gin;
```

> **Warning:** `CREATE EXTENSION` requires superuser privileges. A non-superuser cannot enable extensions — this step **must** be run as a superuser.

> **Warning:** When upgrading Tines, new extensions may be introduced. Always check the release notes before running a migration and enable any new extensions manually **before** starting the upgraded version.

### 3. Apply required session configuration

The following parameters must be configured at the user level by a superuser before Tines starts.

```sql
ALTER USER tines_user SET standard_conforming_strings = on;
ALTER USER tines_user SET intervalstyle = 'iso_8601';
ALTER USER tines_user SET client_min_messages = 'warning';
ALTER USER tines_user SET lock_timeout = '5000';
ALTER USER tines_user SET idle_in_transaction_session_timeout = '65000';
```

> **Note:** If `tines_user` is not a superuser, a superuser must apply these settings in advance — Tines will fail to start if they are not set.

---

## Using a Schema Other Than `public`

By default, Tines creates all objects in the `public` schema and assumes `search_path` remains at its default. To use a different schema:

- Ensure `tines_user` owns the schema or has been granted all privileges on it
- Set the `DATABASE_SCHEMA` environment variable
- Configure the `search_path` on the user, or set `DATABASE_SCHEMA_SEARCH_PATH`

> **Note:** If `search_path` is not configured correctly, Tines will be unable to locate its tables and will fail on startup.

#### OpenSSL FIPS mode

The Tines FIPS containers (`tines-app-with-fips`) are FIPS 140-3 compliant, using a CMVP-validated OpenSSL 3.1.2+ FIPS provider for all cryptographic operations. FIPS 140-3 compliance has been supported since v40.1.

Please refer to the following documentation to learn more about the migration steps and the key differences

## Migrating from tines-app to tines-app-fips images

### Downloading releases from /admin/upgrade

If you are downloading Tines release fro `/admin/upgrade` you will now see a new button to down the tines app dedicated for FIPS, which contains openSSL FIPS. Once you download, there is nothing else you need to do on your end. The upgrade and setup scripts will continue to work as usual with the correct base images (aliased).

### Running Tines in AWS Fargate, Kubernetes or similar environment & Docker Hub

If you are getting the Tines image from Docker Hub and running Tines in AWS Fargate, Kubernetes or similar environment where you need to define the `tines-app` image version in deployment spec, you will now need to start referencing the dedicated image name -  `tines-app-with-fips:$version` instead of `tines-app:$version`. Everything else remains the same. 

## Differences between `tines-app` and `tines-app-with-fips`

- The major difference between the two is `openSSL` build. `tines-app-with-fips` runs with openSSL 3 compiled with openSSL 3 FIPS. 


In case of any questions or concerns, please do not hesitate to reach out to our support team.

## PostgreSQL configuration

You will also need to ensure that you are running PostgreSQL verion **14.x. **

After that, you can spin up your application with `RUN_FIPS=true` (as mentioned above) to start using Tines with OpenSSL FIPS.

Based on the page structure, add this new section **after "Differences between tines-app and tines-app-with-fips"** and **before "PostgreSQL configuration"**:

### nginx FIPS Image

For customers requiring FIPS compliance across their entire deployment stack, Tines also provides a FIPS-enabled nginx image  built on nginx 1.28.

The nginx FIPS image uses the same OpenSSL 3 FIPS provider as `tines-app-with-fips`. TLS termination at nginx and application-level cryptography in tines-app both use the same FIPS-validated module.

#### Accessing action templates in air-gapped Tines deployments

Customers with air-gapped environments which have no outbound internet connectivity can still avail of the action template library. By default, the .ENV file is set to try and connect to the internet to download templates from the Tines S3 Bucket housing the JSON files. This download will fail in air-gapped environments.

Action Templates are also housed in the Postgres database and these can be used to populate the templates in a Self-Hosted air-gapped environment. To enable this functionality follow these steps

Step 1: Update the .ENV accordingly by setting the SYNC_TEMPLATES variable to false as shown below:

![](https://www.datocms-assets.com/55802/1670942149-screen-shot-2022-12-05-at-10-03-40-am.png)

Step 2: Run the upgrade.sh script to enable the functionality. The upgrade script will back up the Postgres DB as well as stop and start all containers.

> **NOTE:** This will cause a brief downtime of your Tines environment.

Step 3: Once the upgrade is complete, navigate to the Tines Storyboard to confirm Action Templates are now present.

### Upgrading Tines

#### Infrastructure Maintenance

This guide is designed to help you upgrade your self-hosted Tines infrastructure and its external components. Before attempting any operations in this guide, we **strongly urge** you to take backups of your database. We also suggest that you do not upgrade Tines versions while making core infrastructure changes.

### Shutdown and Startup of Tines Services

If you are performing maintenance or migrations on the underlying infrastructure hosting Tines, follow the preferred order below to gracefully shut down. Following these steps will cause downtime for your Tines tenant. This order is applicable to Docker Compose, ECS, and Kubernetes. For each step, reduce the number of running instances to zero.

Before starting the shutdown process, review the section **"Checking tines-sidekiq queue size."** If samples show non-zero values that are not reducing, you should scale the `tines-sidekiq` service before starting the shutdown.

**Scale-down order:**

1. **nginx **or other proxy service (if applicable)
2. **tines-app**
3. **tines-sidekiq** (Ensure the queue size reaches near 0 and is not increasing during sampling before proceeding).
4. **tines-command-runner**
5. **Redis/Valkey**
6. **Postgres**
7. **pypi-server**

#### Examples of scaling down by platform:

**Docker Compose:**

```bash
docker compose stop tines-app
```

**ECS:**

```bash
aws ecs update-service --cluster tines --service tines-app --desired-count 0
```

**Kubernetes:**

```bash
kubectl scale deployment tines-app --replicas=0
```

---

### Checking tines-sidekiq Queue Size

Run the following Rails command from within a `tines-sidekiq` container to check the queue size. Run this command multiple times over a 2–5 minute window to ensure the volume is decreasing.

```ruby
bundle exec rails runner "puts JSON.pretty_generate(Sidekiq::Queue.all.map { |q| { name: q.name, size: q.size, latency: q.latency.round(2), paused: q.paused? } })"
```

**Accessing the container:**

- **Docker:** `docker exec -it tines-sidekiq /bin/bash`
- **Kubernetes:** `kubectl exec -it <pod-name> -c tines-sidekiq -- /bin/bash`
- **ECS: **[https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html)

```bash
aws ecs execute-command --cluster \
--task \
--container tines-sidekiq \
--interactive \
--command "/bin/bash
```



### Migrating to a New Database Instance

Before performing operations on your PostgreSQL instance, ensure you have recent backups and snapshots in place. Migration time varies based on data volume and network speed. **Do not** run this process from a laptop or any machine that may enter sleep mode.

**Prerequisites:**

1. All Tines services shut down except Postgres.
2. A host with `pg_dump` and `pg_restore` installed.
3. Network connectivity between source and destination databases.
4. Adequate local storage for the dump file.
5. Existing `DATABASE_NAME` and `DATABASE_HOST` configuration details.

#### Export

```bash
pg_dump -h <existing_db_host> \
        -U <username> \
        -d <database_name> \
        -Fc \
        -v \
        -f tines_migration_snapshot.dump
```

#### Import

```bash
pg_restore -h <new_db_host> \
           -U <username> \
           -d <database_name> \
           -v \
           tines_migration_snapshot.dump
```

---

### Migrating to a New Redis/Valkey Instance

Redis/Valkey is an in-memory, transient datastore that does not require manual migrations or backups. To make cluster changes (such as adding nodes), follow the standard shutdown and startup procedure listed above.

---

### Changing DNS for External Services

Changing the DNS for external services (e.g., Redis/Valkey or Postgres) should **never** be performed while Tines is running.

Update the following values in your configuration:

**Docker Compose and ECS (.env):**

```yaml
REDIS_URL=redis://<new_dns_host>:6379/1
DATABASE_HOST=<new_dns_host>
```

**Helm (values.yaml):**

```yaml
tinesApp:
  databaseConfiguration:
    DATABASE_HOST: <new_dns_host>
  redisConfiguration:
    REDIS_URL: redis://<new_dns_host>:6379/1
```

**Note:** Before restarting Tines, ensure the new DNS entry has propagated and is accessible from your infrastructure. At a minimum, wait for the duration of the DNS TTL and flush any local DNS caches if necessary.

#### Upgrade recommendations

The recommendations in this section are applicable to any self-hosted upgrade, regardless of platform.

## Pre-upgrade recommendations

1. Ensure that your data has been backed up.
2. Plan for some downtime because the upgrade process will stop
3. Read the release notes [here](https://www.tines.com/docs/release-notes/) for any recommendations, including upgrading 1 major version at a time.

## General upgrade steps

1. Download the package from a cloud tenant.
2. Unzip the package to where your tines directory is.
3. Dependent on your self-hosting platform, there will be different requirements on loading new images, updating Redis and Postgresql containers, etc. You can inspect the `upgrade.sh` file for docker-compose commands and apply them to your self-hosted architecture.

#### Upgrading on Docker Compose

> **IMPORTANT:**
> To ensure a smooth upgrade experience and maintain compatibility, we recommend customers follow these sequential upgrade paths:
> 
> For example From version 18.x to 20.x:  
> Upgrade first from 18.x to 19.x.  
> After successfully upgrading to 19.x, proceed to upgrade from 19.x to 20.x.

### Pre-upgrade recommendations

Before upgrading, we advise that you backup your data.

### Step 1. Downloading upgrade package 

You will need to have an active Cloud tenant to access the latest self-hosted version of Tines. Download the Tines installation package using the following link:

https://<your cloud tenant name>.tines.com/admin/upgrade

For example

[https://quiet-sky-3834.tines.com/admin/upgrade](https://quiet-sky-3834.tines.com/admin/upgrade)

The file will be a zip archive named as follows: tines_<build id>.zip. 

Unzip the archive and upload it to the host machine where Tines is installed. 

### Step 2. Move files to the installation directory 

Unzip the installation package and move all the files to the tines installation directory: 

```bash
unzip tines_<build_id>.zip 

cp -rT tines_<build_id>/ /opt/tines  
```

### Step 3. Running upgrade script 

The installation package includes an upgrade script, upgrade.sh. Ensure it is executable with the following command: 

```bash
chmod +x upgrade.sh
```

Run the upgrade script: 

```bash
./upgrade.sh 
```

When the upgrade script runs it will attempt to back up the database. To do this, it will print a list of volumes where the database data may be stored. When prompted, type the correct volume (under default conditions there will only be one). 

 

### Troubleshooting 

If Tines did not start after running ​docker-compose up​, check the console for relevant errors. You can also use steps in our [Troubleshooting Tines on Docker Compose](https://www.tines.com/docs/troubleshooting-tines-on-docker-compose/) to diagnose the issue.

Contact ​**support@tines.io**​ with the output of ​upgrade.sh script and docker-compose logs​.



### Upgrading Redis

Tines 14.0 and above requires Redis 6.2 or above, Tines 31.2 recommends using Redis 7.2 or Valkey 7.2. If you were previously running Tines 13.x or below then you'll need to change the version of the Redis Docker image used in your installation. This can be done at any stage before upgrading to 14.x or above - Redis 6.2 is compatible with all previous Tines versions.

To do this you need to:

1. Open a terminal in your installation directory (typically `/opt/tines`).
2. Run `docker-compose stop` to stop your installation's Docker containers. This will completely stop your installation until you restart the containers later, so be aware that this will cause downtime.
3. Open the `docker-compose.yml` file in your installation directory.
4. `Edit the image` key under the `redis` service in that file to use the image you require, `redis:x.x-alpine`.
5. Save your changes to the `docker-compose.yml` file and run `docker-compose up -d` - this will restart your installation with the new Redis image.

#### Upgrading on AWS Fargate

> **NOTE:**
> Self-hosted customers can safely upgrade to any later minor version of the same or the following major version - i.e. from a 7.x.x version to any later 7.x.x version or any 8.x.x version.
> 
> -   Major version upgrades are only supported between consecutive major versions - i.e. while upgrading from any 7.x.x version to any 8.x.x version will work fine, attempting to upgrade directly from 6.x.x to 8.x.x (skipping 7.x.x) will fail.
> -   "Deprecated" versions are found to contain issues. It is recommended that the last patch of the minor version should be used when upgrading.

### Pre-upgrade recommendations

Before upgrading, we advise that you backup your data.

### **Steps to upgrade Tines for a self-hosted AWS Fargate deployment**

Step 1. Obtain the image from either your Docker Hub account or your cloud tenant at `/admin/upgrade`.

Step 2. Unzip/pull the image.

Step 3. Push the image to your ECR repository.

Step 4. Create a new revision using the new image for each of the `tines-app` and `tines-sidekiq` task definitions.

Step 5. Update each of the `tines-app` and `tines-sidekiq` tasks with their new definition. To ensure no interruption to ongoing story runs, we recommend updating `tines-app` first, ensuring its fully deployed and then updating `tines-sidekiq`.

*Step 6. Optional* - Check the logs to ensure the new tasks are running as expected.

*Step 7. Optional* - Navigate to `/version` on your tenant and check that the version is up to date.



### **Upgrading the Redis version**

Tines 14.0 and above requires Redis 6.2 or above, Tines 31.2 recommends using Redis 7.2 or Valkey 7.2 if using AWS Elasticache. If you need to upgrade your Elasticache Redis cluster then please follow the instructions [**here**](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/VersionManagement.html). Note that this will incur downtime to your instance while the cluster is upgraded. All previous Tines installations are compatible with Redis 6.2, so this upgrade can be performed before upgrading to Tines 14.0.

### Monitoring Tines

Self-hosted Tines operates on a shared responsibility model. Tines supports the application, but the underlying infrastructure, including the database, cache, and compute — is managed by the customer. Monitoring bridges that boundary, giving both sides visibility into what's happening and helping to identify whether an issue originates in the application or the infrastructure it runs on.

Good monitoring practice means catching problems before they affect users, understanding the cause of incidents when they occur, and making informed decisions about capacity.

## What to monitor

Tines monitoring operates at two levels:

**Infrastructure** — the compute, database, and cache that Tines depends on, such as PostgreSQL and Redis. Alerting on CPU, memory, and availability indicates when a deployment is under stress and may need attention. See [Infrastructure metrics](https://www.tines.com/docs/self-hosted/monitoring-tines/infrastructure-metrics).

**Application** — the behavior of Tines itself: web requests, background jobs, database queries, and calls to external services. OpenTelemetry tracing provides this visibility and is especially useful for diagnosing slow or failing stories. See [Application tracing](https://www.tines.com/docs/self-hosted/monitoring-tines/application-tracing).

## Event notifications

Beyond infrastructure and application health, Tines has a built-in event limit system that sends notifications when tenants, stories, or teams approach their daily event limits. Configuring these notifications is important. Hitting an event limit stops stories from running.

Event notifications are configured by tenant owners in [Settings → Event limit settings](https://www.tines.com/docs/admin/event-limit-settings/). Alert thresholds can be set and notifications routed to users or webhooks.

#### Infrastructure metrics

### AWS 

If you are running on AWS we recommend setting up Cloudwatch alarms for the following metrics

- AWS Aurora
  
  - Alert when `CPUUtilization` is above 80% utilization. Reference [here](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraMonitoring.Metrics.html).
  - Alert when `FreeableMemory` is below 80% utilization of the total memory. Reference [here](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraMonitoring.Metrics.html).
- AWS Elasticache
  
  - Alert when `DatabaseMemoryUsagePercentage` is above 80%. Reference [here](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/CacheMetrics.Redis.html).
  - Alert when `EngineCPUUtilization` is above 80%. Reference [here](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/CacheMetrics.Redis.html).
- AWS ALB
  
  - Alert when `UnHealthyHostCount` is above 50% of the desired host count for over 2 mins. For example: If you have set desired task count for `tines-app` to be `2`, then your set the threshold for `UnHealthyHostCount` to be `1`. Reference [here](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-cloudwatch-metrics.html).
  - Alert when `HTTPCode_ELB_502_Count` is above 5 requests. This metric indicates that your load balancer cannot successfully route requests to its backends and you traffic has been dropped. Reference [here](https://repost.aws/knowledge-center/elb-alb-troubleshoot-502-errors).
    
    - If you see frequent occurrences of this alert then increase your desired tasks count.
- AWS ECS Fargate
  
  - Alert when `CPUUtilization` is consistently (5 minutes or more) above 80%. Note: this could also be a sign that you may need to increase the number of tasks on the service. In other words, scale horizontally. Reference [here](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#ECS).

If you find that the alert is frequent and any of the metrics are consistently above the mentioned thresholds then its best to scale up the instance type. For example: If you are on `db.r7g.large` , you should upgrade the Aurora cluster to `db.r7g.xlarge`.

### Non-AWS setup 

For now AWS setups our recommendations are similar to AWS setups. For example

- You should setup monitoring for your storage system if it is occupying more than 80% of total storage.
- You should setup monitoring if the CPU utilization of your compute systems is consistently above 80%.

#### Application tracing

## Tracing 

For deeper application-level observability, we recommend enabling **OpenTelemetry (OTEL) tracing**. This provides visibility into web requests, background job execution, database queries, external API calls, and more — going beyond infrastructure metrics to help you diagnose performance bottlenecks.

At a minimum, set `OTEL_ENABLED=true` on your `tines-app` and `tines-sidekiq` containers and point `OTEL_EXPORTER_OTLP_ENDPOINT` at your collector. Enabling `OTEL_AUTO_INSTRUMENTATION=true` captures a broad set of spans with no additional code changes.

See [OpenTelemetry Traces configuration](https://www.tines.com/docs/self-hosted/configuring-tines/opentelemetry-traces/) for full setup details and collector filter recommendations.

#### Tenant health dashboard

The self-hosted dashboard gives administrators a real-time overview of their Tines deployment's health. It is available at **Tenant health** in the main navigation and is visible to admin users on self-hosted installations.

The dashboard automatically refreshes every 25 seconds. You can pause or manually refresh using the controls in the top-right corner. Polling pauses automatically when the browser tab is in the background.

![](https://www.datocms-assets.com/55802/1774625315-tenant-health.gif)

## Alert banners

Banners appear at the top of the dashboard when something requires your attention:

- **Version mismatch** -- Appears when your running containers are reporting different application versions. This typically means a deployment is still in progress or did not complete successfully.
- **Long-running queries** -- Appears when PostgreSQL has queries running for longer than five minutes, which may indicate stuck transactions.

---

## Version overview

Three cards display the core software versions in your deployment:

- **App version** — the Tines application version reported by your containers. An "Update available" notice appears if a newer release exists.
- **PostgreSQL** — PostgreSQL version.
- **Redis** — Redis or Valkey version.

> **Note:** Keeping all components up to date ensures access to the latest features, performance improvements, and security patches.

---

## Containers

An expandable section lists every registered application container. Use this to verify all containers are running, on the same version, and have healthy connectivity to your database and cache.

Each row shows:

- **Name** — the container's identifier.
- **Type** — the service role (e.g., web, worker).
- **Version** — the application version. Highlighted if it differs from the newest version across containers.
- **Revision** — the git commit revision the container was built from.
- **PG latency** — round-trip time to PostgreSQL. Highlighted above 10 ms.
- **Redis latency** — round-trip time to Redis. Highlighted above 5 ms.
- **Last seen** — how recently the container checked in.

> **Warning:** If a container has not been seen recently or shows a different version from the others, it may need to be restarted or redeployed.

---

## PostgreSQL health

Key indicators of your database's health and performance.

### Connections

Total connections vs. the `max_connections` limit. The number of active connections and utilization percentage are displayed

> **Note:** If utilization is consistently above 80%, consider increasing `max_connections` or reviewing your connection pooling configuration.

### Cache hit ratio

The percentage of data reads served from PostgreSQL's buffer cache rather than disk. A ratio below 99% may indicate that `shared_buffers` is too small for your workload.

### Index hit ratio

The percentage of index lookups served from cache. Low values suggest your working set exceeds available memory.

### Long-running queries

The count of queries running longer than five minutes, along with the duration of the oldest. Long-running queries can hold locks and slow down other operations.

### Latency

Round-trip time for a `SELECT 1` from the application to PostgreSQL. Values above 10 ms are highlighted and may indicate network or resource issues.

### Last vacuum

How long since the most recent vacuum or autovacuum ran across all tables.

> **Warning:** Vacuums should run at least once every 24 hours. Without regular vacuuming, tables become bloated and performance degrades over time.

### Database size

Total size of the database on disk. Useful for capacity planning and tracking growth.

---

## Redis health

Status indicators for your Redis or Valkey instance.

### Memory usage

Current memory consumed by Redis. Monitor for unexpected growth that could indicate a need to scale up.

### Hit ratio

The percentage of key lookups served from cache. A low hit ratio may indicate excessive cache evictions or an undersized instance.

### Connected clients

The number of active client connections to Redis.

### Latency

Round-trip time for a `PING` from the application to Redis. Values above 5 ms are highlighted and may indicate network or resource issues.

---

## Sidekiq health

Visibility into background job processing.

### Workers active

The number of background workers currently processing jobs, shown against the total available. Click to view in-progress jobs.

### Queued jobs

Jobs waiting to be picked up by a worker. Click to view the queue.

### Jobs awaiting retry

Jobs that failed and are scheduled to be automatically retried. Click to view retries.

### Dead jobs

Jobs that permanently failed after exhausting all retry attempts. Click to review dead jobs.

> **Note:** Dead jobs are not retried automatically.

### Queue latency

The time between a job being enqueued and a worker beginning to process it. High latency means workers are not keeping up with demand — consider scaling your worker containers.

### Pending action runs

The number of action runs waiting to execute.

### Troubleshooting

#### Troubleshooting Docker Compose

If you encounter issues during the installation process and require assistance, contact support@tines.io. Please include the [docker-compose logs](https://docs.docker.com/reference/cli/docker/compose/logs/) output.

### Troubleshooting Scripts

Our troubleshooting scripts are designed to help you identify common installation and configuration issues when setting up Tines with Docker Compose.

These scripts perform various checks and troubleshooting steps to provide insights into your environment, configuration, and dependencies.

To troubleshoot common installation and configuration issues, run the troubleshooting scripts:

`<SELFHOSTED_RELEASE_ROOT>` by default is the path `/opt/tines` in on our install package. If you chose a different path, please change this to match your environment.

```bash
<SELFHOSTED_RELEASE_ROOT>/troubleshoot/run.sh
```

To forward the results of these scripts to support at Tines, run the following:

```bash
<SELFHOSTED_RELEASE_ROOT>/troubleshoot/send-results-to-support.sh
```

The following data will be contained in the report:

- Misconfigured or missing mandatory environment variables
- Network connectivity to hq.tines.io (from host and container)
- Database migration and disk space status
- Docker and Docker Compose installations
- Postgres and Redis connection status
- SMTP connection and authentication status
- FIPS status
- Tenant info
  
  - Action limit
  - Daily event limit
  - User limit
  - Story limit
  - Team limit
  - Name
  - Domain
  - User emails

#### Script Overview

1. **Environment Variables Check**: This script checks the presence and validity of essential environment variables required for your Tines configuration. It verifies the completeness and accuracy of variables related to your tenant, email settings, database connections, and core configuration.
2. **Connection to HQ**: This script tests the ability to reach hq.tines.io over the internet. It validates network connectivity and ensures your system can access external resources. The HQ tenant is managed by Tines and serves multiple functions, such as gathering telemetry data and delivering updates for self-hosted clients. Failure to establish a connection with hq.tines.io will result in the unavailability of these functions within your tenant.
3. **Container Health Check**: This script inspects the health and status of your Docker containers. It confirms the proper functioning of key components within the containerized environment such as connections to Postgres and Redis, and SMTP connection and authentication.
4. **Database Checks**: This script performs database-related checks to ensure your Tines application can interact with Postgres as well as the host having sufficient free space available. This job will also print out a read of information about your tenant to the console.

#### Troubleshooting Further

While the troubleshooting scripts cover common issues, they may not address every possible scenario. If you encounter persistent problems or issues not covered by the troubleshooting scripts, consult our official documentation or reach out to our support team (support@tines.io) for additional assistance.

### Restarting Tines[**​**](https://www.tines.com/docs/self-hosting/get-started/installation#restarting-tines)** **

If you need to restart Tines use the following command:

```bash
docker-compose restart
```

If you have modified your .env file and want the changes to take effect, use the following command to restart Tines:

```
sudo docker-compose down && docker-compose up -d
```

> **NOTE:** For custom configuration and deployment, contact your Tines Sales Engineer (SE) or Customer Success Engineer (CSE).



### Log Collection

To collect Tines logs, first get the list of containers running on your host:

```bash
sudo docker container ls
```

Then get the logs for the desired container:

```bash
sudo docker logs (container id) > tines.log
```

### Miscellanous Checks

- When upgrading to a new Tines version, it's important to ensure that the name of the directory from which you're running the upgrade matches the name of the Docker volume, typically `/opt/tines`. If your installation directory differs, verify that the volume name generated during setup by Docker Compose corresponds with the directory name. You can check this by running `docker volume ls | grep db-data`. The prefix before` db-data `should match the directory name.

#### Troubleshooting AWS Fargate

There are a handful of steps to the AWS Fargate deployments that require the setup of networking etc. for each container. We need to create Security Groups in steps with specific ports and then reference these later on, which can lead to confusion and errors in the deployment.

It is important to note that in the ideal architecture configuration HTTPS traffic will come into the AWS Elastic Load Balancer (ELB) and then with the necessary Security Group configured to allow traffic over port 3000 from the ELB to the container that is running the Tines Application. If this is not configured properly you will not be able to access Tines Application UI. The AWS ELB is expected to act as a forwarder of traffic and without the correct port openings, Tines will be unable to do so.

If you try to access the URL and receive error messages such as HTTP 503 or HTTP 504 error code, it is typically a generic error being served up from the[ ELB](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/ts-elb-error-message.html) itself indicating it is having an issue reaching the destination with the HTTPS request, in this case, the Tines Application server. If an actual HTTP error occurred with the Tines Application server itself, indicating traffic was reaching the App, this error would be generated by Tines and written to CloudWatch.

Verify the following:

1. The ELB is allowing HTTPS traffic from the internet
2. The ELB has the appropriate SG (Security Group) configured to allow traffic over port 3000 to the Tines App
3. The same SG allowing traffic in over port 3000 is allowing all traffic out (noted as: 0.0.0.0/0)
4. To confirm which SG is associated with the container, have the user navigate to ECS > Clusters > The Tines Cluster Name They Created > Service: The Tines Service
5. With the Tines deployment in Fargate, the Security Group is attached directly to the service.

![](https://www.datocms-assets.com/55802/1669140053-screen-shot-2022-04-19-at-1-04-01-pm.png)

If you check the CloudWatch logs for the Tines-Application container and see similar log lines below, this indicates that the Tines App is functioning correctly. We would recommend reviewing the hosting network for any possible issues. 

![](https://www.datocms-assets.com/55802/1669140133-untitled.png)

##### Restarting Tines on AWS Fargate

To restart the Tines services in your Fargate deployment using the CLI, first, connect to the AWS CLI and list the services:

```
aws ecs list-services --cluster <cluster_name>
```

Then restart the desired service as follows:

```
aws ecs update-service --cluster <cluster_name> --service <service_name> --force-new-deployment
```

To restart the Tines services using the ECS GUI, perform the following steps:

1. Log into the AWS Management Console.
2. Navigate to the Elastic Container Service.
3. Navigate to Clusters.
4. Click on the cluster that your Tines service is in.
5. Click on the service you’d like to restart.
6. Click Update.
7. Check “Force new deployment”.
8. Click Skip to review.
9. Click Update service.

##### Log Collection on AWS Fargate

To collect the AWS CloudWatch logs for a Tines service, perform the following steps:

First, get the list of log groups with the following command:

```
aws logs describe-log-groups
```

Next, get the list of log streams with the following command:

```
aws logs describe-log-streams
```

Then run the following command with the desired log group name and the most recent log stream for that group:

```
aws logs get-log-events \

   --log-group-name (log group name) --log-stream-name (log stream name) \

   --output text > tines-service-log.log
```

#### Troubleshooting Run Script

## Troubleshooting

This guide contains some common commands and areas to check in order to troubleshoot any setup issues with your self-hosted run script deployment using `tines-command-runner` and `tines-app`.

### docker-compose

**exec into **`**tines-command-runner**`** container**

```bash
docker ps (grab the <tcr_container_id>)
docker exec -it <tcr_container_id> /bin/bash
```

**Review docker logs for **`**tines-command-runner**`

```bash
docker logs -f <tcr_container_id>
```

**Stop / Start all services**

```bash
docker compose down && docker compose up -d
```

**Stop only tines-command-runner service**

```bash
docker stop <tcr_container_id>
docker start <tcr_container_id>
```

**Review docker env variables for tines-command-runner:**

```bash
docker inspect <tcr_container_id> | jq '.[0].Config.Env'
```

or if `jq` is not installed:

```bash
docker inspect <tcr_container_id> | grep -A 20 "Env"
```

### Environment Variables

**tines-command-runner**

- `PIP_INDEX_URL`: Specifies the primary Python Package Index. Default is `https://pypi.org/simple`. This can be overridden to use a custom package index.
- `PIP_EXTRA_INDEX_URL`: Allows specifying a fallback package index. Default is `http://pypi-server:8080/simple/`, which is the default local PyPI server(`pypi-server`) running alongside the command runner.
- `NO_PIP_INDEX`: If set, disables all package indexes, including `PIP_INDEX_URL` and `PIP_EXTRA_INDEX_URL`, relying only on packages included in the container.
- `TRUSTED_HOST`: Specifies a trusted package index host. Default is `pypi-server`, ensuring that the local PyPI server is trusted.
- `LOG_LEVEL` - Configures the logging level for the Python harness. Set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` (defaults to `INFO`). Set `LOG_LEVEL=DEBUG` to enable debug logging for troubleshooting. Logs are written to `/tmp/tcr-logs/harness-{environment_id}-{stdout,stderr}.log`.
- `UV_NATIVE_TLS` [optional] - When set (e.g. UV_NATIVE_TLS=1), uv uses the platform's native TLS certificate store. This is used to enable corporate proxies or custom CAs in the system store. When not set the variable is not used.

**tines-app**

- `TINES_COMMAND_RUNNER_PORT` - defaults to `4400`
- `TINES_COMMAND_RUNNER_HOST` - defaults to `tines-command-runner`

**capacity configuration**

`tines-command-runner` can be configured to handle different levels of concurrent requests through two key environment variables:

- `TINES_COMMAND_RUNNER_WORKERS` - Controls the number of Puma worker processes. (Default is 2).
- `TINES_COMMAND_RUNNER_THREADS` - Controls the number of threads per worker. (Default is 5).

These values determine the total concurrent requests the command runner can handle (workers × threads). For example, with default values, the service can handle 10 concurrent requests (2 workers × 5 threads).

### APP_USER

Ensuring `APP_USER` can `sudo` into Users

As part of the permissions structure for run script we generate users within in the container via the Dockerfile.

This has a number of variables which can be overridden if necessary:

```bash
APP_USER=${APP_USER:-2000}
NUM_USERS=${NUM_USERS:-1000}
GROUP_ID=${GROUP_ID:-1000}
GROUP_NAME=${GROUP_NAME:-tines-team-group}
```

**Check User Existence in **`**/etc/passwd**`

```bash
getent passwd | grep tines-tcr-team-user
```

**Check for Users Created with Specific UID Range:**

```bash
awk -F: '$3 >= 2000 && $3 < 3000 {print $1, $3}' /etc/passwd
```

**Verify Home Directories Exist**

```bash
ls -ld /home/tines-tcr-team-user-*
```

**Check Group Membership**

```bash
getent group tines-team-group
```

**Check file write permissions for a user (in the case of permissions errors)**

```bash
sudo -u tines-tcr-team-user-2000 touch /specify-the-dir-path
```

### UID Mappings

**Check permissions on team-uid-mappings**

```bash
ls -ld /team-uid-mappings
cat /team-uid-mappings
```

**View UID to Username Mapping (for All Users)**

```bash
getent passwd
```

**Check UID for a Specific User**

```bash
id tines-tcr-team-user-2000
```

### Connectivity

This command should be run from within the `tines-app` container. It will attempt to connect to the `tines-command-runner` in a way that’s very similar to how the `tines-app` itself does it. It loads the host and port and then makes a POST request to the endpoint.

```bash
curl -vvv "http://${TINES_COMMAND_RUNNER_HOST:-tines-command-runner}:${TINES_COMMAND_RUNNER_PORT:-4400}/run_script" \
    -H "Content-Type: application/json" \
    -d '{
        "action_id": 1,
        "script_type": "python",
        "script_content": "def main(input_data):\n    print(f\"Received input_data: {input_data}\")\n    result = \"2\" + str(2)\n    return \"result is \" + str(result)\n",
        "pre_run_command": "uv pip install $TINES_DEPENDENCIES_LIST",
        "dependencies": [],
        "environment_id": "1234",
        "team_id": 100,
        "timeout": 60
    }'
```

### Best Practices

#### Data backup recommendations

We recommend daily data backups are configured for all platforms, ideally in a separate environment to where Tines is hosted. This separate environment could be made more reliable by being hosted in a different region, separate infrastructure, etc.

These steps will be varied for different platforms, so it's best to consult your platform's guidelines for specific instructions. We have detailed instructions for our supported platforms below.

## AWS

### AWS RDS

For AWS RDS we recommend that you turn on backups following the instructions [here.](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ManagingAutomatedBackups.html#USER_WorkingWithAutomatedBackups.BackupWindow) It is best to set the backup retention period to 7 days and to ensure the backup frequency is at least daily.

If you're using AWS Aroura, you can follow the steps [here](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Managing.Backups.Retaining.html).

### AWS Elasticache

The data that lives in Redis inside AWS Elasticache is very short lived. So nightly backups are not really that helpful. For high availability we recommend that you turn on Multi-AZ option on your AWS Elasticache Cluster. You can follow the instructions [here.](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/AutoFailover.html#AutoFailover.Enable)

## Docker Compose

### Step 1: Copy the data to a backup folder

The first step is to copy the data to a file, which will then be transferred later. The exact instructions will vary depending on how your environment is configured, but here are some examples.

#### Example 1: using pg_dump

If you have the PostgreSQL client utility `pg_dump` , you can generate a dump file following instructions [here](https://www.postgresql.org/docs/14/backup-dump.html).

#### Example 2: copying the data directory

Make a backup directory if it doesn't exist, e.g., `mkdir db-backup`.

Copy the data to this new directory, e.g., `cp -r /var/lib/docker/volumes/tines_db-data/_data/ ./db-backup/2025-01-01/`. You may need to change the name of the Tines database directory if you've configured it with a different name.

### Step 2: Transfer the file to a separate system

You can now transfer the backup folder to a separate environment. There is an example below of how you could do this.

#### Example: using scp

You can use [secure copy](https://linux.die.net/man/1/scp) to transfer to a remote server. E.g., `scp ./db-backup/2025-01-01/ backup@remote-server:/backup/2025-01-01`.

### Step 3: Automating this backup

The first two steps were manual ones to create a backup. In order to automate this process on a recommended daily basis, this could be set up to run automatically. Again, this is dependent on what platform you're on, but one way to implement this would be cron jobs on linux using a bash script with the commands above.

#### Data encryption

We recommend that you turn on [encryption at rest and transit for your database](/docs/self-hosted/configuring-tines/database-field-level-encryption/) through your setup and/or service provider as applicable. In addition, we also recommend that you take advantage of field level encryption which is an opt-in Tines feature. You can learn more more about that [here](/docs/self-hosted/configuring-tines/database-field-level-encryption/).

#### Utilizing database reader endpoints

The Tines application now supports the use of reader instances within your Database cluster. To ensure optimal performance, Tines optimizes queries directed to the primary writer instances. However, for enhanced efficiency, some queries are redirected to the reader instances where appropriate.

To enable this feature, you can set the `DATABASE_READONLY_ENDPOINT` environment variable to your reader cluster endpoint. After setting this variable, a new deployment or a restart of your containers is required for the changes to kick in.



This works with any setup that supports a reader instance in your Database cluster including AWS Fargate and AWS Aurora based setups.

#### Migrating RDS to Aurora

If your Tines installation is running a single RDS database instance, we recommend you migrate to an Aurora PostgreSQL database cluster for improved performance and reliability.

You can migrate the database using a snapshot by following [this guide from AWS](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Migrating.html#AuroraPostgreSQL.Migrating.RDSPostgreSQL.Import.Console). 

> **NOTE:** You can also [migrate using an Aurora read replica](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Migrating.html#AuroraPostgreSQL.Migrating.RDSPostgreSQL.Replica). This approach offers less downtime for your application, but is more complex.

1 - While you execute this migration, you should update your **tines-app** and **tines-sidekiq** ECS services to have a desired task count of 0 in order to prevent data loss. This will require some downtime on your Tines installation.

2 - Follow all steps in AWS's guide. When you reach step 5, follow these guidelines to configure the new cluster correctly:

- Your new database cluster should use the same values as your existing instance for the following fields:
  
  - *VPC*
  - *DB subnet group*
  - *VPC security group*
- We also recommend the following values for other fields:
  
  - *DB engine version* - the latest minor version for the same major version as your existing instance. This will be a PostgreSQL 11 version, unless you have already manually migrated your existing instance to PostgreSQL 14.
  - *DB instance class *- we recommend either a `db.t4g.large`, or a `db.r6g.xlarge` depending on your performance needs. Larger instance sizes will also work if you need further performance improvements, although they  will cost more.
  - *DB instance identifier* - a clear name like `tines-db-cluster`.
  - *Public access *- this should be set to *No* for security.
  - *Availability Zone* - we recommend *No preference* for improved reliability.
  - *Database port* - this can be left as the default of 5432, unless you have customized it on your existing instance. This should match the `DATABASE_PORT` environment variable for your installation.
  - *Enable Encryption* - we recommend enabling this to ensure your Tines data is encrypted at rest.
  - *Auto minor version upgrade* - we recommend disabling this to avoid unexpected downtime on your Tines installation.

3 - Update your `.env` file in S3:

- `DATABASE_HOST` should be set to the Endpoint URL of your new database cluster.
- All other fields can keep the same values as before.

4 - Update your **tines-app** and **tines-sidekiq** ECS services to have their original desired task count. Once these tasks start, your Tines installation will be available again.

#### Multi-AZ Aurora Cluster

> **NOTE:**
> ### Prerequisites
> 
> -   Ensure you have an Aurora PostgreSQL database cluster already set up with one instance. This step is covered [here](https://www.tines.com/docs/self-hosted/deploying-tines/aws-fargate/deployment-guide/#step-4-create-a-postgres-database).
> -   Retrieve the **db-cluster-identifier** of your previously created aurora cluster.

## Configuration Steps

### Step 1. **Verify Current Aurora Cluster Configuration**

Ensure that your current Aurora PostgreSQL cluster is correctly set up:

```bash
aws rds describe-db-clusters --db-cluster-identifier <your-cluster-identifier>
```

### Step 2. **Create a Second Instance in a Different Availability Zone**

- Identify the current Availability Zone (AZ) of your existing instance.
- Choose a different AZ for the new instance to enable Multi-AZ deployment.

```bash
aws rds create-db-instance \
  --db-instance-identifier <new-instance-identifier> \
  --db-cluster-identifier <your-cluster-identifier> \
  --engine aurora-postgresql \
  --db-instance-class db.t4g.large \
  --availability-zone <new-availability-zone> \
  --db-subnet-group-name tines-db
```

### Step 3. **Enable Automatic Failover**

- Verify that the new instance is part of the cluster and located in a different AZ.
- Ensure the cluster is set to automatically failover to the secondary instance in case of a failure.

```bash
 aws rds modify-db-cluster \
  --db-cluster-identifier <your-cluster-identifier> \
  --apply-immediately \
  --multi-az-enabled 
```

### Step 4. **Configure Cluster Parameters for High Availability**

- Modify the cluster parameter group to optimize settings for high availability.

```bash
 aws rds modify-db-cluster-parameter-group \
  --db-cluster-parameter-group-name <your-cluster-parameter-group> \
  --parameters "ParameterName=auto_failover_enabled,ParameterValue=1,ApplyMethod=immediate"
```

### Step 5. **Verify Multi-AZ Configuration**

- Ensure the cluster is now Multi-AZ with automatic failover enabled.

```bash
 aws rds describe-db-clusters --db-cluster-identifier <your-cluster-identifier>
```

#### Migration & Disaster Recovery (Fargate)

This document outlines how to migrate your Tines self-hosted tenant to another deployment. It explains how to take an RDS Snapshot, delete the old database, and restore the RDS Snapshot to a new deployment. 

In the event of a disaster, **Restoring an RDS Snapshot** outlines how to restore your Tines tenant. 

# Taking an RDS Snapshot

Step 1: Go to the [AWS Console](https://console.aws.amazon.com/).

Step 2: Go to **Elastic Container Service**.

Step 3: Go to the appropriate Cluster.

Step 4: You will see that you have the *tines-app* and *tines-sidekiq* services running.

Step 5: Go to **RDS**.

Step 6: Select **Databases** in the menu on the left of the screen.

Step 7: Go to the appropriate database.

Step 8: Go to the Actions dropdown in the upper right corner of the screen.

Step 9: Select **Take Snapshot**.

Step 10: Give the snapshot a name and click **Take Snapshot**. You will see that the snapshot is being created. Please allow time for the operation to complete. 

# Deleting the Database (Optional)

Step 11: Go to **Databases**. Go to the appropriate database.

Step 12: Go to the Actions dropdown in the upper right corner and select **Delete**. The database will be deleted. This may take a few minutes, please allow it time to complete. 

# Restoring an RDS Snapshot

Step 13: Select the snapshot. Go to the **Actions** dropdown in the upper right corner and select **Restore Snapshot**.

Step 14: Enter the name of the DB instance.

Step 15: Select the Existing VPC Security Group for the DB instance.

Step 16: Proceed with restoring the snapshot. The restoration of the snapshot may take a few minutes. Please allow the operation time to complete. 

Step 17: Go to the **Elastic Container Service** and select the appropriate Cluster.

Step 18: Select both tasks (*tines-app*, *tines-sidekiq*) and stop the tasks. The task definitions are set to restart the tasks if they are down, so wait for the tasks to come back up.

### Release notes

> **TIP:** Get notified of new releases by following the [Self-hosting release notes RSS feed.](https://www.tines.com/rss/tines-self-hosting-release-notes.xml)



## Upgrade compatibility note:

> **NOTE:**
> Self-hosted customers can safely upgrade to any later minor version of the same or the following major version - i.e. from a 7.x.x version to any later 7.x.x version or any 8.x.x version.
> 
> -   Major version upgrades are only supported between consecutive major versions - i.e. while upgrading from any 7.x.x version to any 8.x.x version will work fine, attempting to upgrade directly from 6.x.x to 8.x.x (skipping 7.x.x) will fail.
> -   "Deprecated" versions are found to contain issues. It is recommended that the last patch of the minor version should be used when upgrading. The feature set in the deprecated version will be included in the last patch unless explicitly excluded in the patch notes.

> **IMPORTANT:** **Action required:** Self-hosted customers should upgrade to Redis/Valkey 7.2 by June 9, 2026. New Tines releases will no longer support Redis 6.x after this date.

### May 19th, 2026 - 41.0.0

```
Image digests for build e90895d9_v41_0_0:
tines/tines-nginx:e90895d9_v41_0_0 sha256:aa8159e15ba56e5d5690b66afbb3047173fd600b4ec23a799695a3c3bfbf6c3f
tines/tines-nginx-unprivileged:e90895d9_v41_0_0 sha256:c5511f46c9fdf11bc2b3016e37293e3095c47355ef309bb02f7f1188f81d21a6
tines/tines-app:e90895d9_v41_0_0 sha256:129ef59c14fbf6fb069fb4e80d41dc8896b18300563a0c2f88f7b0a18ae454f2
tines/tines-app-with-fips:e90895d9_v41_0_0 sha256:434f0c8dbe138af24b9ac854a12c927cdd1386e40dbcbed1e6bd1bd2c2024e4d
tines/tines-command-runner:e90895d9_v41_0_0 sha256:c5428d846f4f4f65bf57e75267bff0e3ebe9f8339761fc91208ca84491c06df0
tines/tines-command-runner-fips:e90895d9_v41_0_0 sha256:ef4836e446d39ac956e445de5415197f876c6adf40564ad1d4080944915e0ce6
```

- [Increased record limits](https://www.tines.com/whats-new/increased-record-limits)
- [Open Cases in Workbench permission](https://www.tines.com/whats-new/open-cases-in-workbench-permission)
- [Global date filter for dashboards](https://www.tines.com/whats-new/global-date-filter-for-dashboards)

### May 15th, 2026 - 40.7.2

```
Image digests for build 440981e0_v40_7_2:
tines/tines-nginx:440981e0_v40_7_2 sha256:5ee7cb9aa4e2b92584c4354cb32a053e9a0aec9956f5aba34997a3ece1b69da6
tines/tines-nginx-unprivileged:440981e0_v40_7_2 sha256:2df68ec036381da3ba7f1cd5a05afe942315e73a0a86ac8ba0abf5a24d449429
tines/tines-app:440981e0_v40_7_2 sha256:d07d1dc2372b5ef2549868a2bcbb01360416e91df02678a67df81fb826464366
tines/tines-app-with-fips:440981e0_v40_7_2 sha256:7325f6ac3f8b7573c98cba7c5f21e5ab8267ba93bbe22b8d4b227f3ce643c1fb
tines/tines-command-runner:440981e0_v40_7_2 sha256:c1adb1abef8ad3e7354140e464abd3f0d5c6438bf758d2fb56725eb9bedf95e6
tines/tines-command-runner-fips:440981e0_v40_7_2 sha256:5e1faf0ecb82fad647d792d0424c7913ea678547a00c9dedf7e28d0be60375f2
```

- Members of case groups can now use case templates from their parent team, matching what the UI already shows instead of failing with “template not found.”

### ~~May 13th, 2026 - 40.7.1~~[DEPRECATED IN FAVOR OF 40.7.2]

```
Image digests for build e0e02c40_v40_7_1:
tines/tines-nginx:e0e02c40_v40_7_1 sha256:564cd8769943dca3a3056f7283165777ed019a387feff209c78758d3b86dedef
tines/tines-nginx-unprivileged:e0e02c40_v40_7_1 sha256:246bba42020b3f60985d9d0ff15bfbda253c1b0bec272dddc0e4119b7a5bad15
tines/tines-app:e0e02c40_v40_7_1 sha256:b639377f6f4a674a26444184297e6d2c425dca69fb677a0a5f3ac477b136aab0
tines/tines-app-with-fips:e0e02c40_v40_7_1 sha256:f993e87a11bdd44c86565886695599526fe9fc553878df6301193db5245c2a5d
tines/tines-command-runner:e0e02c40_v40_7_1 sha256:d53befba13e81036f670fb8c09d7c2fb87911e75ecf144c6a355a8e96fa37e02
tines/tines-command-runner-fips:e0e02c40_v40_7_1 sha256:84afaaa64da8e8e69a72dc9437f0971c0baee185a0e1474acc5b2eb3b01d065e
```

- Making Tines better every day with minor fixes and optimizations.

### 
~~May 12th, 2026 - 40.7.0~~[DEPRECATED IN FAVOR OF 40.7.2]

```
Image digests for build 66f9c078_v40_7_0:
tines/tines-nginx:66f9c078_v40_7_0 sha256:496fffd320ee604544e7d1bd962ea3c1819548baa8934bc71e5fc904b11c7c6f
tines/tines-nginx-unprivileged:66f9c078_v40_7_0 sha256:50ab8c9c1ce9ee873426a1e5f995035994d5d8f1140e3180705f9c1f68a9eacd
tines/tines-app:66f9c078_v40_7_0 sha256:20203ff4b13e08d85030ae5d1ef11fc2e9edf13afc3251ce936c29208156151f
tines/tines-app-with-fips:66f9c078_v40_7_0 sha256:8930f9b9a3505ef47e21dba5719beac65128fd7d25c80da5a204f671680c807e
tines/tines-command-runner:66f9c078_v40_7_0 sha256:d46600605dc3546ace115b765fb7ff9b10ec3f89750c486f65bb7b34a1ee4dd9
tines/tines-command-runner-fips:66f9c078_v40_7_0 sha256:3fa09d0b52a9e5cde166ca60827a1de2ed3fa3b4d60efd193461b8f6e580aca0
```

- [Nested folders](https://www.tines.com/whats-new/nested-folders)
- [Folder icons](https://www.tines.com/whats-new/folder-icons)
- [Block remote images in case content](https://www.tines.com/whats-new/block-remote-images-in-case-content)
- [Fine-grained permissions for Cases](https://www.tines.com/whats-new/fine-grained-permissions-for-cases)
- [MCP tool support for Workbench](https://www.tines.com/whats-new/mcp-tool-support-for-workbench)
- [Enhanced retention options for records](https://www.tines.com/whats-new/enhanced-retention-options-for-records)
- [Cases multiplayer editing](https://www.tines.com/whats-new/cases-multiplayer-editing)
- [Cases CSV export can now match saved views](https://www.tines.com/whats-new/cases-csv-export-can-now-match-selected-list-view-columns)
- [Order and filter records by "Updated at"](https://www.tines.com/whats-new/order-and-filter-records-by-updated-at)

### May 5th, 2026 - 40.6.0

```
Image digests for build b28d8773_v40_6_0:
tines/tines-nginx:b28d8773_v40_6_0 sha256:6dce326ff688b1ab74797f1f7318e7416b02f506010560d1c348a1110532a4d6
tines/tines-nginx-unprivileged:b28d8773_v40_6_0 sha256:9b185fc990984458ca3c6ecda4b493277eeca558f6e4056e6e5c8a7a3e4572f2
tines/tines-app:b28d8773_v40_6_0 sha256:68e5eeb7cb2ab8545b1798f1f75a33c3bc8342cb6977b343d6ce3eed3953048c
tines/tines-app-with-fips:b28d8773_v40_6_0 sha256:41838cd6a80847dbb0d300f93d771cb69ba99e575770d40215cbc34f85c9e6b2
tines/tines-command-runner:b28d8773_v40_6_0 sha256:d89c0ed82aea1ef113f693248654f9703939e20a32b6db492e4dae63c3637df2
tines/tines-command-runner-fips:b28d8773_v40_6_0 sha256:5d6dea29f75ece80a173e79070027e12557117ceda2a5a262d21021d2d5c5eb2
```

- [Load child records from the Record GET tile](https://www.tines.com/whats-new/load-child-records-from-the-record-get-tile)



### April 29th, 2026 - 40.5.1

```
Image digests for build 67197d78_v40_5_1:
tines/tines-nginx:67197d78_v40_5_1 sha256:3569f8704f560019652f4b6a0f47ab1f14266c782f7f2c340c456edff2816bb9
tines/tines-nginx-unprivileged:67197d78_v40_5_1 sha256:2cb2af09167938bfa7183675dc70866fbcd1f13a9c611035bc130ddbff408a6d
tines/tines-app:67197d78_v40_5_1 sha256:232436b180503a0d6dd4cb8c13167310164455e4f27a73b17f24f926b83af29d
tines/tines-app-with-fips:67197d78_v40_5_1 sha256:3f07b0b8b421782319c733675ddb79e426e0446fa28df96afdf81d773a47ab84
tines/tines-command-runner:67197d78_v40_5_1 sha256:6392f88ff3ea3a88bb6fb0ccdabdc82722092d308568dabb66ff69bcbfebe9a6
tines/tines-command-runner-fips:67197d78_v40_5_1 sha256:a415ed4422e913d98dfd987ec411c1e79b6464997dd59b31892690fb348b99c7
```

- [Use full API endpoint URL for custom AI providers](https://www.tines.com/whats-new/use-full-api-endpoint-url-for-custom-ai-providers)
- [Anthropic through Google Vertex](https://www.tines.com/whats-new/anthropic-through-google-vertex)
- [GPT 5.5 now available in Tines](https://www.tines.com/whats-new/gpt-5-5-now-available-in-tines)
- [Stories list filtering updates](https://www.tines.com/whats-new/stories-list-filtering-updates)
- [Link cases from mentions](https://www.tines.com/whats-new/link-cases-from-mentions)
- [Toggle headings in story notes](https://www.tines.com/whats-new/toggle-headings-in-story-notes)
- [Updated change control notifications](https://www.tines.com/whats-new/updated-change-control-notifications)
- [Story copilot file attachments](https://www.tines.com/whats-new/story-copilot-file-attachments)
- [Custom cron schedules for dashboard snapshots](https://www.tines.com/whats-new/custom-cron-schedules-for-dashboard-snapshots)

### April 24th, 2026 - 40.4.2

```
Image digests for build 3f79f5f2_v40_4_2:
tines/tines-nginx:3f79f5f2_v40_4_2 sha256:e320871963a3f94596839af8676dd65842841128a8afe050e14335b3812f1790
tines/tines-nginx-unprivileged:3f79f5f2_v40_4_2 sha256:5baaef7581ae7427e7874a7c2f8aa452a87cce0c0eb327fc14f4f17f23421bbe
tines/tines-app:3f79f5f2_v40_4_2 sha256:c7690df0b705caa35ddd94bc695e7522849783ad33554d7e600ffce80fbcaa70
tines/tines-app-with-fips:3f79f5f2_v40_4_2 sha256:65bf947954c2ca488eec099d1600ddbb3cc578ae4818a20c50a33949d8b6e211
tines/tines-command-runner:3f79f5f2_v40_4_2 sha256:3ff7f9c167eb632175401b9b2f315f94740a0df25c21e9432af71f4e73b5f238
tines/tines-command-runner-fips:3f79f5f2_v40_4_2 sha256:e3f8f9a341dfd7f00005f887415b2df9c2f020750e88d5860d420a7bdac1de55
```

- Fix for issues with GPT 5.4 on some custom AI providers.

### April 22nd, 2026 - 40.4.1

```
Image digests for build 9d30ea47_v40_4_1:
tines/tines-nginx:9d30ea47_v40_4_1 sha256:cb67406c056258d6f4e54f20f7c9061d745dbfec84888e2705fc7389a13b1080
tines/tines-nginx-unprivileged:9d30ea47_v40_4_1 sha256:f39c8b3dd499ad5844cbfeb9ad237e84b758eed6464571e8f1ee6638b0827f7e
tines/tines-app:9d30ea47_v40_4_1 sha256:e4104ab0918150d8b2e4b1dbbbbe7c1cf61059fb0faa9d57fb2926940f5b07bb
tines/tines-app-with-fips:9d30ea47_v40_4_1 sha256:8504050aaa69b625b37482f75d1481b803e3ad5e72f77bccc99705a933d20f13
tines/tines-command-runner:9d30ea47_v40_4_1 sha256:3a9a224164bcec0d8717b30a5f00ef5907d0abaf3d3f577321e8a94f90e94430
tines/tines-command-runner-fips:9d30ea47_v40_4_1 sha256:daa6e3a1edc3f965171a1664bf45dee1b8a4d2ce925af6871f5138c6c59f757b
```

- Fixes connect flows where a blank modal was appearing instead of the popup when connecting a credential

### ~~April 21st, 2026 - 40.4.0~~[DEPRECATED IN FAVOR OF 40.4.1]

```
Image digests for build 15b8ece1_v40_4_0:
tines/tines-nginx:15b8ece1_v40_4_0 sha256:307a55a41665146ad341989c68e88bf7a8ef4d8e71cecf954a7cdc7d8a48bade
tines/tines-nginx-unprivileged:15b8ece1_v40_4_0 sha256:e1a0d394ef4c2bb671f88f6cede43f3033fd4de626fd19a624c9c11c10d10f2c
tines/tines-app:15b8ece1_v40_4_0 sha256:466fdf5f1539f3a5c79ce68c81e4e0a088ca31d63323dd5c6846f84e7a26db59
tines/tines-app-with-fips:15b8ece1_v40_4_0 sha256:4112c47b25a5c7c7fd82df69fa5d1926afb41d5e2f235a370d0470f05692f334
tines/tines-command-runner:15b8ece1_v40_4_0 sha256:f84d578560e2b35056abbe4d7cd2cd034c241bc552b818b09f5eb5b5919b25d2
tines/tines-command-runner-fips:15b8ece1_v40_4_0 sha256:bea10184256022432fa334600308b04f16ee562610067d90ee9389126ada90ca
```

- [YAML support in Workbench](https://www.tines.com/whats-new/yaml-support-in-workbench)
- [Claude Opus 4.7 now available in Tines](https://www.tines.com/whats-new/claude-opus-47-now-available-in-tines)
- [AI tool output truncation controls](https://www.tines.com/whats-new/ai-tool-output-truncation-controls)
- [Live logs in drafts](https://www.tines.com/whats-new/live-logs-in-drafts)
- [`OBJECTS\_TO\_MARKDOWN\_TABLE` function](https://www.tines.com/whats-new/objectstomarkdowntable-function)
- [Add descriptions to record type fields](https://www.tines.com/whats-new/add-descriptions-to-record-type-fields)



### April 14th, 2026 - 40.3.0

```
Image digests for build 41971ec9_v40_3_0:
tines/tines-nginx:41971ec9_v40_3_0 sha256:270a3bb3d3282c20e8ae4c9c8abcd7a5fc1533506ef0b9facf24998746097ec7
tines/tines-nginx-unprivileged:41971ec9_v40_3_0 sha256:e57359cd011c5dea1c3de5511acce2b1826bdf89e402b552f74b0058c46becc2
tines/tines-app:41971ec9_v40_3_0 sha256:53faab081b2ee5088b5158a1391b2b87d4b8ab37ffc1060cf40544732adcf5ed
tines/tines-app-with-fips:41971ec9_v40_3_0 sha256:6b7eee2581b306049c51f764b09055f0dd69ae72d2152988ac4230e3e3d8f930
tines/tines-command-runner:41971ec9_v40_3_0 sha256:580bfc4cc8d7f32390ecc91ce5d16f3a1633cee07de871e6b857597f990439ed
tines/tines-command-runner-fips:41971ec9_v40_3_0 sha256:ff2610533f609fb68aeb896e51897ffedd2f0682e06b5458f62f1b372c6cd7a4
```

- [Additional request parameters for AI agent actions](https://www.tines.com/whats-new/additional-request-parameters-for-ai-agent-actions)
- [Team-level access controls for AI providers](https://www.tines.com/whats-new/team-level-access-controls-for-ai-providers)
- [Granular Page Collections permission](https://www.tines.com/whats-new/granular-page-collections-permission)
- [Rich text copy paste support for Workbench](https://www.tines.com/whats-new/rich-text-copy-paste-support-for-workbench)
- [Expand case sidebar sections](https://www.tines.com/whats-new/expand-case-sidebar-sections)
- [Bulk close cases with closure requirements](https://www.tines.com/whats-new/bulk-case-closure-requirements-override)
- [Return up to 500 cases per page with the list API](https://www.tines.com/whats-new/increase-cases-list-500)
- [Improved query params for case actions](https://www.tines.com/whats-new/improved-query-params-for-case-actions)
- [OAuth as an Access Control option when building an MCP Server](https://www.tines.com/whats-new/oauth-as-an-access-control-option-when-building-an-mcp-server)

### ~~April 10th, 2026 - 40.2.2~~[DEPRECATED IN FAVOR OF 40.3.0]

```
Image digests for build 5824ecce_v40_2_2:
tines/tines-nginx:5824ecce_v40_2_2 sha256:c76903ee1d5577fae077bd3079e1f62516d788ba066c58a03fd7b0cca8ddc311
tines/tines-nginx-unprivileged:5824ecce_v40_2_2 sha256:fa85930d87502f981cc2d897ce69ee4d4bcd247dea6d7415a32b75fc40a26c5c
tines/tines-app:5824ecce_v40_2_2 sha256:71e90f5f9cb1e5ed39f9a98d52c9c1f033404850d430fc3e317e7b6338c1647a
tines/tines-app-with-fips:5824ecce_v40_2_2 sha256:481023c8866bbd9db39aeb5f4df1dfb7db953416ad87620cd2f613a0b583a148
tines/tines-command-runner:5824ecce_v40_2_2 sha256:5638cf984f0089a280cb3338e0bb16e34bf3fbddad46e95a7095c9debe110315
tines/tines-command-runner-fips:5824ecce_v40_2_2 sha256:041de089bc3db1515ec4d4f4ff67d7dd7e144b70d4dfe51bc30455c260284d81
```

- Fixes an issue where case creation was blocked by a removed database column still being referenced

### April 9th, 2026 - 39.5.1

```
Image digests for build 756b74b5_v39_5_1
tines/tines-nginx:756b74b5_v39_5_1 sha256:51d0f642206989be5002eb561bd94615e19397acd9954f5706f62a3bbbe01e2d
tines/tines-nginx-unprivileged:756b74b5_v39_5_1 sha256:1e613c7d208b23161bf00dbe0582f5380873aa8f99887a6c5a12aea24f938e8f
tines/tines-app:756b74b5_v39_5_1 sha256:4e391637eb98f7d8be42f2d9f9cfb39e4f093e84d81fe09611189a27f3f46ed7
tines/tines-app-with-fips:756b74b5_v39_5_1 sha256:990633174054ba8bbb34f1ecf20306dde8cbd9b6e3246dc3f98a579e4ff8b4dd
tines/tines-command-runner:756b74b5_v39_5_1 sha256:44b5969cb6ae172e22e519e24f7c703b852fc92f38cfe1d58b8e303c94811508
tines/tines-command-runner-fips:756b74b5_v39_5_1 sha256:1b9bdad8eb0fd29496324cf1b8b665272cb9697a7c853716234c7575afc7aa72
```

- Fixes an issue where the ALLOW_PRIVATE_REQUESTS environment variable was not respected when examining ip addresses

### ~~April 9th, 2026 - 40.2.1~~[DEPRECATED IN FAVOR OF 40.3.0]

```
Image digests for build 22672740_v40_2_1:
tines/tines-nginx:22672740_v40_2_1 sha256:88098cf9ae1254a2daa98b175921687d89da4d86077739999a656eb00d6b62a9
tines/tines-nginx-unprivileged:22672740_v40_2_1 sha256:9c1ab306831afdd58f251fa863e7dc75c2790075c4902351caa6d45be8a1f22d
tines/tines-app:22672740_v40_2_1 sha256:568210733bf79ac30c0240af903b1734aa2ed28377ad5013003aea754f9a16f4
tines/tines-app-with-fips:22672740_v40_2_1 sha256:84b5bed5078da31af30e7cdd2a11db522a6f3673ca19fca93fadeca27a80487e
tines/tines-command-runner:22672740_v40_2_1 sha256:623a14d4e41115d514a8fed0b43562edf6afb743163de10b398636ae2cac0dcc
tines/tines-command-runner-fips:22672740_v40_2_1 sha256:701ac200863b1108791e27afb84b973ccc017e838afae491333368dcf7a57dbe
```

- Fixes an issue where the ALLOW_PRIVATE_REQUESTS environment variable was not respected when examining ip addresses

### ~~April 7th, 2026 - 40.2.0~~[DEPRECATED IN FAVOR OF 40.3.0]

```
Image digests for build 1adb42f3_v40_2_0:
tines/tines-nginx:1adb42f3_v40_2_0 sha256:d3cb85e839633b6affe226327b93a726e03aad9d1e87be89dced95fc6da83e7e
tines/tines-nginx-unprivileged:1adb42f3_v40_2_0 sha256:282855d41e605f2d32a7a8ec1baa60bfdc7cc69ef60de4ce1421b356ac8bb7b1
tines/tines-app:1adb42f3_v40_2_0 sha256:e21f2d4a4f8ef7e720b5034ee1d1712ef0c37e01e71e56b8c58543734a00a1f8
tines/tines-app-with-fips:1adb42f3_v40_2_0 sha256:7350bdf097ca9bda7f78721575029b3819c8d05ec49f06d28c8b6ee69382ea66
tines/tines-command-runner:1adb42f3_v40_2_0 sha256:f093f92b31e88d739813285d4cd3ac417e9fbbd3d9655c6346a58c8c7964af93
tines/tines-command-runner-fips:1adb42f3_v40_2_0 sha256:8cd7d30033fd10ac6157aea680e5c6483c4fef4b8bb073e4d3867ec1d0ca58c0
```

- [Display dashboard chart values](https://www.tines.com/whats-new/display-dashboard-chart-values)
- [AI tenant overview](https://www.tines.com/whats-new/ai-overview)
- [Credential expiry API](https://www.tines.com/whats-new/credential-expiry-api)
- [Filter private templates by product](https://www.tines.com/whats-new/filter-private-templates-by-product)
- [Toggle individual AI features](https://www.tines.com/whats-new/toggle-individual-ai-features)
- [API endpoint to move private templates](https://www.tines.com/whats-new/api-endpoint-to-move-private-templates)
- [Template explorer refresh](https://www.tines.com/whats-new/template-explorer-refresh)
- [Credential connect flows reinstated](https://www.tines.com/docs/credentials/connect-flows/)



### ~~March 31st, 2026 - 40.1.0~~[DEPRECATED IN FAVOR OF 40.3.0]

```
Image digests for build 47545d66_v40_1_0:
tines/tines-nginx:47545d66_v40_1_0 sha256:6db397562d2726297a53b479bb93dac62354e9bd4b4a05abc97d0c4bf08a8792
tines/tines-nginx-unprivileged:47545d66_v40_1_0 sha256:8f3909450a421ea47312cde6211efbc6aa0951f8c4829f472ad8389f0b652619
tines/tines-app:47545d66_v40_1_0 sha256:d35ce21e64382a0c53724c7175b25645f6369e3ddf519fa723c185634e48494f
tines/tines-app-with-fips:47545d66_v40_1_0 sha256:7a48825ddbc1a710ebe00f4f0c68bf56bd80245dd4a74b4e7458b51f5c2e7564
tines/tines-command-runner:47545d66_v40_1_0 sha256:cc15844f966f1cec77537d933fb20ee1415021863bd2af6fb29b51d4b178c337
tines/tines-command-runner-fips:47545d66_v40_1_0 sha256:e26417539dcd3ce9e486ce7fca5f321450271dafb1c3696efea8cfcb84fd4426
```

- [`PAD\_LEFT` & `PAD\_RIGHT` functions](https://www.tines.com/whats-new/padleft-and-padright-functions)
- [Connect AI Agent actions to remote MCP Servers with OAuth](https://www.tines.com/whats-new/connect-ai-agents-to-remote-mcp-servers-with-oauth)
- [Tenant health dashboard update](https://www.tines.com/whats-new/tenant-health-dashboard-update)
- [Offline Documentation](https://www.tines.com/whats-new/offline-documentation)



### ~~March 24th, 2026 - 40.0.0~~[DEPRECATED IN FAVOR OF 40.3.0]

```
Image digests for build a8b97a8b_v40_0_0:
tines/tines-nginx:a8b97a8b_v40_0_0 sha256:022be75aa9037f3a39e4084f67433eee9a19fab42af2be0df23bc5ce634dd610
tines/tines-nginx-unprivileged:a8b97a8b_v40_0_0 sha256:9e3d028a1e0c7c81bae7920c10f5c8c9513431b84fc7801e8d95ec9d838a9cc8
tines/tines-app:a8b97a8b_v40_0_0 sha256:ea92ac6bf596b7e5debe0f4a122c7fc526653e62225f1f149eb383ee8f4732af
tines/tines-app-with-fips:a8b97a8b_v40_0_0 sha256:70368870e154a3fade648de0ac49dae6ed34f8e56226daa552bc0917eedcb72f
tines/tines-command-runner:a8b97a8b_v40_0_0 sha256:ebeba9183909bedd934d34a4910007cf031390bb586b0580d3710e270081213c
tines/tines-command-runner-fips:a8b97a8b_v40_0_0 sha256:aa1637392cc4913a2ddf599763164fd02fbb63291106b9e0e5d9d33ff1f4f5bc
```

- [Trigger action is now called Condition action](https://www.tines.com/whats-new/trigger-action-is-now-called-condition-action)
- [Import and export dashboards via API](https://www.tines.com/whats-new/import-and-export-dashboards-via-api)
- [Records action and API improvements](https://www.tines.com/whats-new/records-action-and-api-improvements)
- [Improved storyboard note markdown](https://www.tines.com/whats-new/improved-storyboard-note-markdown)

### 
~~March 17th, 2026 - 39.5.0~~[DEPRECATED IN FAVOR OF 40.3.0]

```
Image digests for build 4384f713_v39_5_0:
tines/tines-nginx:4384f713_v39_5_0 sha256:f4b0fcfabecf38ee660b68f5fcd79bc16ec3703bae0623968597662c33f2491f
tines/tines-nginx-unprivileged:4384f713_v39_5_0 sha256:3a6c2dde73a6ba07a297dfe76c3b9b1c4a16cc59a990b74af0311fc06476b3ae
tines/tines-app:4384f713_v39_5_0 sha256:16b51428a7f5afe6c2614fc05ad4422b9696c6f226c7127f3e0407f60ced7f94
tines/tines-app-with-fips:4384f713_v39_5_0 sha256:bc7802cb6807656487126b2ca4b43fa86ec7dd723a3f4917a9c6ab649254548b
tines/tines-command-runner:4384f713_v39_5_0 sha256:0abcdc75ae3970a4925692f0f2179d2257ff7fc7b4d8552a1966ef73207cab2d
tines/tines-command-runner-fips:4384f713_v39_5_0 sha256:ba051c298674c5f1aaaa0ce40674f59f1a984317f8e1c6bfb0efeda3a81996f7
```

- [Updating AI model names](https://www.tines.com/whats-new/updating-ai-model-names)
- [Enhanced resources API: locking and conditional updates](https://www.tines.com/whats-new/enhanced-resources-api-locking-and-conditional-updates)
- [Custom time range filters for record reports](https://www.tines.com/whats-new/custom-time-range-filters-for-record-reports)
- [Import and export case templates](https://www.tines.com/whats-new/import-and-export-case-templates)
- [Story copilot support for private templates](https://www.tines.com/whats-new/story-copilot-support-for-private-templates)
- [Move templates between teams](https://www.tines.com/whats-new/move-templates-between-teams)
  

### 
March 10th, 2026 - 39.4.0

```
Image digests for build 803989f4_v39_4_0:
tines/tines-nginx:803989f4_v39_4_0 sha256:a03f0b03e6749dab94c1a474e15c4c669c764a55163203ea595a543f9537076b
tines/tines-nginx-unprivileged:803989f4_v39_4_0 sha256:f4ea8a4b40d1d03b4781fb25c5fbff88f3593c8be739aa8a22eb8eb1c92869de
tines/tines-app:803989f4_v39_4_0 sha256:bc939a7f0ad949eafee45a7197f6e821eb8f48efea81ccca432142194e18c948
tines/tines-app-with-fips:803989f4_v39_4_0 sha256:cc42d43e4b27bf2fccd7afb10438379fbcc10fef109a1fa987c934980ed15048
tines/tines-command-runner:803989f4_v39_4_0 sha256:f51316ca5c1615182907802407f87e1092bc2ffff38a68c4ecbcc9c8373bb74a
tines/tines-command-runner-fips:803989f4_v39_4_0 sha256:f7eef6289a3df2658aa6570e98afd5357a33247583eebe394d86a5dad83b3c87
```

- [Easily reference headers via `META`](https://www.tines.com/whats-new/easily-reference-headers-via-meta)
- [New API endpoint to delete story links](https://www.tines.com/whats-new/new-api-endpoint-to-delete-story-links)
- [Refreshed page elements toolbar](https://www.tines.com/whats-new/refreshed-page-elements-toolbar)

### March 3rd, 2026 - 39.3.0

```
Image digests for build d210109e_v39_3_0:
tines/tines-nginx:d210109e_v39_3_0 sha256:ee4268cf147b429c9573b8f1d318805fbef8f841c9c87afba306e0bf00227b61
tines/tines-nginx-unprivileged:d210109e_v39_3_0 sha256:6745aeaafc4d0c6b8fef1ecab0340bf782c28cee7c1b408b2ebac5bc99508cd8
tines/tines-app:d210109e_v39_3_0 sha256:7b41be865a88b22590fc1ab90b3752d075d7c59e6ed5d72bd8e7c512daf7dce4
tines/tines-app-with-fips:d210109e_v39_3_0 sha256:aa5ead9b882b692ebfe3e6fe1201620093bf114a0d7eec33d944a73023586803
tines/tines-command-runner:d210109e_v39_3_0 sha256:1618d1e2f5bd53aedecb763b09e1bad743d2009d527d8d4d7dd45daeea49ae30
tines/tines-command-runner-fips:d210109e_v39_3_0 sha256:fb4105e78b013f4e2598708602479799cfb2f918c6d9beb2ff0d4b79e2044fa3
```

- [Team scoped private templates](https://www.tines.com/whats-new/team-scoped-private-templates)
- [View stories used by record types and add an icon](https://www.tines.com/whats-new/view-story-usage-and-add-icons-for-record-types)
- [Case field groups](https://www.tines.com/whats-new/case-field-groups)
- [Storyboard sections](https://www.tines.com/whats-new/storyboard-sections)
- [Multi-team API keys](https://www.tines.com/whats-new/multi-team-api-keys)

### March 16th, 2026 - 38.5.4

```
Image digests for build b59376a0_v38_5_4:
tines/tines-app:v38.5.4 sha256:ab31769cca4ce40565c283fe766f4b4f619cbc8de9e46cb3f7a72cd2b17da97b
tines/tines-command-runner:v38.5.4 sha256:29ba07216e2061d97f5ebd43f82052ad13b7f816d37593e60e489b3b008e7d69
tines/tines-nginx:v38.5.4 sha256:62ed71137ab5da1af4154f2ed2e49f13a5430ba86d3f67b98199daf37b757185
```

- [Revised] Fixes a migration issue where self-hosted instances using non-superuser database roles encountered permission errors during schema updates.
  

### ~~March 13th, 2026 - 38.5.3~~[DEPRECATED IN FAVOR OF 38.5.4]

```
Image digests for build 41354a8e_v38_5_3:
tines/tines-app:41354a8e_v38_5_3 sha256:edc46dfc5256d9ee3b23a339cd9f46a6881aad56de11d40cc58f6b739a4af79a
tines/tines-command-runner:41354a8e_v38_5_3 sha256:9c4eb7d6cb0b6c532d91949a083d159a1e2efdfb0546cde1f4436649322a8366
tines/tines-nginx:41354a8e_v38_5_3 sha256:7ca46e24d504a6eb29c8b8e52ba026eea0d7baa0b92b35bd2dd8711eb2344dc6
```

- ~~Fixes a migration issue where self-hosted instances using non-superuser database roles encountered permission errors during schema updates.~~

### February 27th, 2026 - 38.5.2

```
Image digests for build 60594b65_v38_5_2:
tines/tines-app:60594b65_v38_5_2 sha256:39175df7ee70461c184ea1ef0c466cb079a404e144ebe58ac84588bb3c7fa3a0
tines/tines-command-runner:60594b65_v38_5_2 sha256:b2186fb76a21f31f55235b948dfbc853981cf5cfeee9f6008f83cdba7aaa575b
tines/tines-nginx:60594b65_v38_5_2 sha256:22e35e6be0b4ae64a7c482421a394def28ede5c1ee87be3cda144529fe6bedd7
```

- Fixes a bug preventing members of a case group from adding or removing tags on a case

### February 24th, 2026 - 39.2.0

```
Image digests for build ce649a2d_v39_2_0:
tines/tines-nginx:ce649a2d_v39_2_0 sha256:a98ec3fd7425cecbb6c42c9cc4e7e8ce601182b165a6d30d80af93e218937e9a
tines/tines-nginx-unprivileged:ce649a2d_v39_2_0 sha256:0d482645197f0c664a372e6ddb632f1f9d8d7ad9caca2e7e426a66f56a9f53cc
tines/tines-app:ce649a2d_v39_2_0 sha256:1dacfc90eb26ca5bc8de24f95639a85e699511ea8c9b6d085b1290560645bf7f
tines/tines-app-with-fips:ce649a2d_v39_2_0 sha256:8b62ac13737f7b6ea1374a3cbcf19f8bcc36ebdbfaf265278650fd3db4e8ad24
tines/tines-command-runner:ce649a2d_v39_2_0 sha256:7e53d5e5e0f2664db05bd229f62440233fc8415051bf8ee64451db7ef24bde52
tines/tines-command-runner-fips:ce649a2d_v39_2_0 sha256:c29ced96162b03f8b09f5f0796060c85396fb92ed1f145e489ce34cdb247615b
```

- [Add Mistral AI support](https://www.tines.com/whats-new/add-mistral-ai-support)
- [Add parameters to private templates via API](https://www.tines.com/whats-new/add-parameters-to-private-templates-via-api)

### February 19th, 2026 - 39.1.1

```
Image digests for build 0b9d5b0b_v39_1_1:
tines/tines-nginx:0b9d5b0b_v39_1_1 sha256:69ae731f1aa2b6846e34512bb98aa6c31a9b6b726897d8612ed43ed28ee8914e
tines/tines-nginx-unprivileged:0b9d5b0b_v39_1_1 sha256:782ed67efe055a729191d69408d59dfe910246f7a06e16027e836efaf377c802
tines/tines-app:0b9d5b0b_v39_1_1 sha256:7b793d9508ccd6f4d098c0130e1d7bcee6a72961ef2c69169c6ad58d2368a846
tines/tines-app-with-fips:0b9d5b0b_v39_1_1 sha256:5f31cabcecbfaae29e96310895a178772ff665e4f10fb11ea20a0707d69597a0
tines/tines-command-runner:0b9d5b0b_v39_1_1 sha256:e2c0678462d91c78c7a8c3695c107f7d04deabd22cd392a501a060ffe16903e4
tines/tines-command-runner-fips:0b9d5b0b_v39_1_1 sha256:f60c90ba5f5cd66ce7ce3d8c74a155d404e35e913a756bd91af66e3a0ca807f8
```

- Fixes a bug causing action run fair orchestration not to respect the `SIDEKIQ_CONCURRENCY` environment variable



### ~~February 17th, 2026 - 39.1.0~~ [DEPRECATED IN FAVOR OF 39.1.1]

```
Image digests for build c5a9787a_v39_1_0:
tines/tines-nginx:c5a9787a_v39_1_0 sha256:c7ad861fb3f0dda61de18b717e0f6540fa602d0a467cf1d46934392d66b9b517
tines/tines-nginx-unprivileged:c5a9787a_v39_1_0 sha256:9d32839632991a3998e45a9ebad0698f677063170cb8a3c62d0a73be2e6dec87
tines/tines-app:c5a9787a_v39_1_0 sha256:7fe889f41f9c50caba1c778047a33cfe058e0fe44ca4e9d67ce8f757a0ce5452
tines/tines-app-with-fips:c5a9787a_v39_1_0 sha256:e2827b1d08996537fab6030f009ef9576cd5a34a1e5ddc14adaf78dcef79cd31
tines/tines-command-runner:c5a9787a_v39_1_0 sha256:42daa25c87cc464523e91a9b736e46fd494cfbc33b962729da081b339cbb052b
tines/tines-command-runner-fips:c5a9787a_v39_1_0 sha256:fea784b0ad94726f43f0aedb0084423ae629094bf36c66a2916d3d4520ba4a41
```



- ~~This release updates the base image we use for docker. Customers customizing containers or building on top of our containers are encouraged to thoroughly test this version.~~
- [~~Record charting improvements~~](https://www.tines.com/whats-new/record-charting-improvements)
- [~~Duplicate record types within or across teams~~](https://www.tines.com/whats-new/duplicate-a-record-type)
- [~~Duplicate a resource~~](https://www.tines.com/whats-new/duplicate-a-resource)
- [~~Configure story open point~~](https://www.tines.com/whats-new/configure-story-open-point)
- [~~Collapsible table of contents in Cases~~](https://www.tines.com/whats-new/collapsible-table-of-contents-in-cases)
- [~~Import Records from a CSV File~~](https://www.tines.com/whats-new/import-records-from-a-csv-file)



### ~~February 10th, 2026 - 39.0.0~~ [DEPRECATED IN FAVOR OF 39.1.1]

```
Image digests for build ae4ac3bb_v39_0_0 
tines/tines-app:ae4ac3bb_v39_0_0 sha256:70c23ed6fb2a1f70e0b5ec519d91a219372bfdae15a014f4b03d68c83917166c
tines/tines-command-runner:ae4ac3bb_v39_0_0 sha256:f0194fa6c4130e813b5c0cdd253febebb70c6f47c3c4752b006af6812df5c003
tines/tines-nginx:ae4ac3bb_v39_0_0 sha256:d77529504df3ea188280f611cf7efbf3de1b63be73bf0706bb5d7b950a68ce01
```

- [~~January connect flow update~~](https://www.tines.com/whats-new/january-connect-flow-update/)
- [~~January template update~~](https://www.tines.com/whats-new/january-template-update/)
- [~~Update case fields in bulk~~](https://www.tines.com/whats-new/update-case-fields-in-bulk/)

### 
~~February 17th, 2026 - 38.5.0~~ [DEPRECATED IN FAVOR OF 38.5.2]

```
Image digests for build 7d108d1b_v38_5_0 
tines/tines-app:7d108d1b_v38_5_0 sha256:6eb8d5db48980bbfda2f14d2ef35f38cf0cbe132005a397959ecff4b8245f488
tines/tines-command-runner:7d108d1b_v38_5_0 sha256:cf67e82697f1065eeff33aed1886aa245b90a2755e12f7d9a5b7f409cd1681ca
tines/tines-nginx:7d108d1b_v38_5_0 sha256:091079766ed82bff34317e57cacb970165ffabde8251e8dc3ce22dde26d1541a
```

- [Introducing story copilot](https://www.tines.com/whats-new/introducing-story-copilot)
- [Configurable case sidebar](https://www.tines.com/whats-new/configurable-case-sidebar)
  
  - Users may need to clear their browser cache for preferences to be saved.
- [Append to existing content using the Cases API](https://www.tines.com/whats-new/append-to-existing-content-using-the-cases-api)
- Fixed a bug that potentially allowed records to be attached to cases from another teams. Case creation actions that attach Records from a different team will now fail with an error.
- Security fixes

### ~~January 30th, 2026 - 38.4.1~~ Deprecated in favour of 38.5.0

```
Image digests for build b5b9ad83_v38_4_1 
tines/tines-app:b5b9ad83_v38_4_1 sha256:32773f09d9cc9027864bec60b976329900e74ed3a7e0ad1bfc8b8899815585e8
tines/tines-command-runner:b5b9ad83_v38_4_1 sha256:880421b04b063935917409236709ac4c02c2cef6381f6c21ca2df38af3428684
tines/tines-nginx:b5b9ad83_v38_4_1 sha256:114e8c7a57d08faffda9f6ad363aef7cadbd37a9d42c29626ef4bbc489d9e35f
```

Allows for credential selection for self-hosted environments using AWS Bedrock as an AI Provider.

### January 27th, 2026 - 38.4.0

```
Image digests for build 11d2a7d2_v38_4_0 
tines/tines-app:11d2a7d2_v38_4_0 sha256:3903ca28f2c6c3c062b41da39e2723c2b27c6746d3c156e72ddcdad8701a6147
tines/tines-command-runner:11d2a7d2_v38_4_0 sha256:76980c38b79538c6ea132ac8f681bc7b5e8a0df6d7ae44d366ea430b2642fd05
tines/tines-nginx:11d2a7d2_v38_4_0 sha256:745d0eeaf26a1a6a013ee7372c78679abc21a2fed7e1c107008a4677dc00f472
```

- [Track individual event paths](https://www.tines.com/whats-new/track-individual-event-paths/)
- [Easier sharing of cases saved views](https://www.tines.com/whats-new/easier-sharing-of-cases-saved-views)

### January 20th, 2026 - 38.3.0

```
Image digests for build fc0bb3c6_v38_3_0 
tines/tines-app:fc0bb3c6_v38_3_0 sha256:5fe01433aa3f77b05850a72fe54b072efe042b38bed6387481bbd7e342152102
tines/tines-command-runner:fc0bb3c6_v38_3_0 sha256:a6248202a2783e6bf24f79a9309f33806d43aa481503f62822dd824ef7a97f3d
tines/tines-nginx:fc0bb3c6_v38_3_0 sha256:0236907902dc48efa1fc218e1e46f42a0930d438eea2957f08425781a7ff8ed1
```

- [Presence indicators for note blocks](https://www.tines.com/whats-new/presence-indicators-for-note-blocks)
- [`GROUP_BY` now supports returning an array](https://www.tines.com/whats-new/group-by-now-supports-returning-an-array)
- [Keyboard shortcut (d) to disable/enable actions](https://www.tines.com/whats-new/keyboard-shortcut-d-to-disable-enable-actions)
- [Multiple case webhooks with selective notifications](https://www.tines.com/whats-new/multiple-case-webhooks-with-selective-notifications)
- [New permission: Transition case status](https://www.tines.com/whats-new/case-status-permission)

### January 20th, 2026 - 38.2.2

```
Image digests for build 015fc356_v38_2_2 
tines/tines-app:015fc356_v38_2_2 sha256:262aa50b5c92e6ffcaa1a4cacfe863a675c7b3e5b38f6072ecae591e60162bcd
tines/tines-command-runner:015fc356_v38_2_2 sha256:56dc3c7f8ce2435ff87bb6914577b758ce326b30d98409b5d205b746322f864f
tines/tines-nginx:015fc356_v38_2_2 sha256:eef9a25815927ae5e66575621ff2f36505d72dd36d38d96bde8dbc8cba1815a1
```

- Fixes a bug where Stripe.js was loading when self-hosted.

### ~~January 16th, 2026 - 38.2.1~~  Deprecated in favour of 38.2.2

```
Image digests for build cac4ca8a_v38_2_1
tines/tines-app:cac4ca8a_v38_2_1 sha256:848be160cf3dc0fb5d0f277707f865fbb6654399d782d53f2975f493865f855f
tines/tines-command-runner:cac4ca8a_v38_2_1 sha256:ab5c14da5edd8d79e8ca0cd83ba44b143c11e1da8556ebc77f2c2b932da1cba1
tines/tines-nginx:cac4ca8a_v38_2_1 sha256:ab7ae34a7cf37b619f9eb40c22d488d77e45b2ad8640ea6f726d2cd93bdba90e
```

- Fixes a bug when upgrading between major versions.

### ~~January 13th, 2026 - 38.2.0~~  Deprecated in favour of 38.2.2

```
Image digests for build 217d5f92_v38_2_0
tines/tines-app:217d5f92_v38_2_0 sha256:095cc03c7b7a09b4a9676221d60e1e3bd18053ca39be701cba740d1c35431268
tines/tines-command-runner:217d5f92_v38_2_0 sha256:0a5cb7e63631b5ddc99511187d40d3523e406386739b4c262fe298684c844805
tines/tines-nginx:217d5f92_v38_2_0 sha256:7fb7f7b365c27af1a490344c31059f2c509cd051116784be71ba4704cebe97b3
```

- [Advanced default values for option elements](https://www.tines.com/whats-new/advanced-default-values-for-option-elements)
- [Change request API now includes participants](https://www.tines.com/whats-new/change-request-api-now-includes-participants)
- [Mermaid diagrams for Cases](https://www.tines.com/whats-new/mermaid-diagrams-for-cases)
- [Longer retention options for Records](https://www.tines.com/whats-new/longer-record-retention-options)
- [Filter records with more precision within a story](https://www.tines.com/whats-new/filter-records-with-more-precision-within-a-story)
- [Customize Collections with our newest features](https://www.tines.com/whats-new/customize-collections-with-our-newest-features)
- [Credential Modal UI Refresh](https://www.tines.com/whats-new/credential-modal-ui-refresh)
- [Multiple Case webhooks with selective notifications](https://www.tines.com/whats-new/multiple-case-webhooks-with-selective-notifications/)

### January 6th, 2026 - 38.1.0

```
Image digests for build 67ed9f6b_v38_1_0
tines/tines-app:67ed9f6b_v38_1_0 sha256:d01d08ee581bfa91f3819bafc580f24fdddb53f05d20b863ff19ec50b3e96423
tines/tines-command-runner:67ed9f6b_v38_1_0 sha256:6201fd522ff2b25b0a6f0419824e6f15bc4d7dbf3809fad8b0ae8a63dbf2e1ba
tines/tines-nginx:67ed9f6b_v38_1_0 sha256:9109c2d8cfc44f0e9ed71bbfd315efa344f84ab8e75cfe015ada8e2780aad435
```

- [Mermaid diagrams](https://www.tines.com/whats-new/mermaid-diagrams)
- [Set maximum segments for bar and pie charts](https://www.tines.com/whats-new/set-maximum-segments-for-bar-and-pie-charts)
- [Auto-ignored fields in Test Runs](https://www.tines.com/whats-new/auto-ignored-fields-in-test-runs)
- [Emit events on no-match in Extract mode](https://www.tines.com/whats-new/emit-events-on-no-match-in-extract-mode)

### December 16th, 2025 - 38.0.0

```
Image digests for build 082e095d_v38_0_0
tines/tines-app:082e095d_v38_0_0 sha256:54bc03cdc522ce8faa381a8550dcfd3d5351514f1d309a641a26a51ec85be409
tines/tines-command-runner:082e095d_v38_0_0 sha256:0b8e199b40c1355ee1ca7723648c9a296537a7f812c7980a7bbff118d32fd790
tines/tines-nginx:082e095d_v38_0_0 sha256:49d26fa453d8c63acf7eaa4e2542a04c54e0d425e1e7ffff95eacc84b4c4f1d6
```

- [Tines tunnel support for AI Agent action with MCP tools](https://www.tines.com/whats-new/tines-tunnel-support-for-ai-agent-action-with-mcp-tools)
- [Preserve newlines in Multi Request credential secrets](https://www.tines.com/whats-new/preserve-newlines-in-multi-request-credential-secrets)
- [Workbench memory](https://www.tines.com/whats-new/workbench-memory)
- [Dashboard chart drill down](https://www.tines.com/whats-new/dashboard-chart-drill-down)
- [Tenant health page](https://www.tines.com/whats-new/tenant-health-page)

### March 16th, 2026 - 37.3.8

```
Image digests for build c8e66fad_v37_3_8
tines/tines-app:c8e66fad_v37_3_8 sha256:4c40c0bc2920234283167f46a4365650da718f2473368e28839fb9ac00c7c16e
tines/tines-command-runner:c8e66fad_v37_3_8 sha256:035d0bc234f6ea1dc660f6de89b270846c62e1f146640e70d721ec279e07a506
tines/tines-nginx:c8e66fad_v37_3_8 sha256:e3fe907b30ea99a24751b5e29a31f771670bdada0a3828eff2e97a895f88f7a9
```

- [Revised] Fixes a migration issue where self-hosted instances using non-superuser database roles encountered permission errors during schema updates.
  

### ~~March 12th, 2026 - 37.3.7~~[DEPRECATED IN FAVOR OF 37.3.8]

```
Image digests for build 17bf273c_v37_3_7
tines/tines-app:17bf273c_v37_3_7 sha256:303143238e64e00ef3142b258c68c6b7f897fd8be1a6eb425230d25aea3388b1
tines/tines-command-runner:17bf273c_v37_3_7 sha256:d3d8f01f6ec7e92f3b25fec19ef49267e6359b623e0bf406e0a769bb8b4ff9a0
tines/tines-nginx:17bf273c_v37_3_7 sha256:41fc5ced68497d243a12074b20e2b4867ee2374d210cfb3f31527222274d4711
```

- ~~Fixes a migration issue where self-hosted instances using non-superuser database roles encountered permission errors during schema updates.
  ~~

### February 17th, 2026 - 37.3.6

```
Image digests for build 43fcabd4_v37_3_6
tines/tines-app:43fcabd4_v37_3_6 sha256:8b343ad08048c6ae408b0a3d7825eddc6f21bccddd857b130a2e979557a5417e
tines/tines-command-runner:43fcabd4_v37_3_6 sha256:784768ab005123d3cf3a54eb7f3f223b9836f4fb4de78f19719bb4912158cddb
tines/tines-nginx:43fcabd4_v37_3_6 sha256:0a8daccd5fd4dee9d3776ae07026be025a5f9f6b54b85cb8faa00311f5846b39
```

- Security fixes

### ~~January 27th, 2026 - 37.3.5~~ Deprecated in favour of 37.3.6

```
Image digests for build c71dcee4_v37_3_5
tines/tines-app:c71dcee4_v37_3_5 sha256:a60f5921ee4835a782797b1b4140fd76761c10293994611679260fd054db6850
tines/tines-command-runner:c71dcee4_v37_3_5 sha256:4a9bd4e099266dbbaedfae5c1903e31b007deaaa80c7f2707122d1e4e71749b5
tines/tines-nginx:c71dcee4_v37_3_5 sha256:1c46ac22a881bc304149dbf959899999cb38616c5d40bcba00e0681c1f50abaf
```

- Fixes a bug when running script actions via `tines-command-runner` with Python 3.13.

### January 16th, 2026 - 37.3.4

```
Image digests for build 2495e155_v37_3_4
tines/tines-app:2495e155_v37_3_4 sha256:e8a9a343b4eaada90b86c8dff7148a92c3522b149d9cff94a5179d4ff96f1bc1
tines/tines-command-runner:2495e155_v37_3_4 sha256:0a086046cb3436418ba0b878c4834aeba9a3338e431f29e3b95795686ec52692
tines/tines-nginx:2495e155_v37_3_4 sha256:7c30bf741aa28278a246fc351ad4d81e73438926b4d1edbda70c96c395e9c00c
```

- Fixes a bug when upgrading between versions 36 to 37.

### December 12th, 2025 - 37.3.3

```
Image digests for build 5dcf760d_v37_3_3
tines/tines-app:5dcf760d_v37_3_3 sha256:84256ce320610cd7f69d7f0b824a37df1b046b9d7883b28618e01a0ca3ed5813
tines/tines-command-runner:5dcf760d_v37_3_3 sha256:e5c8f8b17b11cb270a7474a59b3ef24bbda6dda2bb39867140e4ebe2751ce6fd
tines/tines-nginx:5dcf760d_v37_3_3 sha256:acec367aa8d4ff395af1e801b888d1c0623da73f844531b352e6b5f75156834d
```

- Fixes default runtime for Run Script actions

### December 11th, 2025 - 37.3.2

```
Image digests for build b560e196_v37_3_2:
tines/tines-app:b560e196_v37_3_2 sha256:2dd0b4f06444946316293cd975868aca21100668bf53c70126f982eb0adf06b6
tines/tines-command-runner:b560e196_v37_3_2 sha256:4a90701f2e09e64c4c19f8a3ad24788509a813c2bc95fcf8ccb33d74e1c475a3
tines/tines-nginx:b560e196_v37_3_2 sha256:51d1bb062e545437d402b661dcee176de83234973e08b48ca50fc2e18f0070c5
```

- Fixes a redirect handling issue when connecting to remote MCP servers via the AI Agent action

### 
December 9th, 2025 - 37.3.1

```
Image digests for build ccaebba1_v37_3_1:
tines/tines-app:ccaebba1_v37_3_1 sha256:08bb9b8bc65b93d681a32d60b738606aadb4fa64ac135dea268d51b524c029c0
tines/tines-command-runner:ccaebba1_v37_3_1 sha256:8e4e11814a3f920c849695a6cdec8d234e539e9aa68e19582d582b986354552c
tines/tines-nginx:ccaebba1_v37_3_1 sha256:1f7080065add04ece1a2449b55ad20cb163cb28279fbcd092cccd28b6b0d3b58
```

- Fixing an SSL issue for Tines Server when providing SSL cipher options

### ~~December 9th, 2025 - 37.3.0~~ Deprecated in favour of 37.3.1

```
Image digests for build 9e920f8e_v37_3_0:
tines/tines-app:9e920f8e_v37_3_0 sha256:03de11672594f09c9697388e3a8099c46315237beb569187a0214ab728ce24f8
tines/tines-command-runner:9e920f8e_v37_3_0 sha256:8703f7f2a082e8ea4c64c39b19cc4e88cbb5c18aa42631e93ed425318ba973ae
tines/tines-nginx:9e920f8e_v37_3_0 sha256:6f1d713517fa5db3c694b48c26af9c4ada61828d494cd7d6cad12b07e54954fb
```

- [Preserve newlines in HTTP credential secrets](https://www.tines.com/whats-new/preserve-newlines-in-http-credential-secrets)
- [Custom headers for the MCP client](https://www.tines.com/whats-new/custom-headers-for-the-mcp-client)
- [Additional filter operators for Case search](https://www.tines.com/whats-new/expanded-filter-operators-for-cases)
- [Mention blocks in Cases](https://www.tines.com/whats-new/mention-blocks-in-cases)
- [Record views API](https://www.tines.com/whats-new/record-views-api)
- [New filter operators for Records](https://www.tines.com/whats-new/new-filter-operators-for-records)
- [Updated activity filter UI in Cases](https://www.tines.com/whats-new/updated-activity-filter-ui-in-cases)

### December 5th, 2025 - 37.2.2

```
Image digests for build 7dc418f7_v37_2_2
tines/tines-app:7dc418f7_v37_2_2 sha256:dd87f10dc828abac4372bfaaadb134cca295c8ed24243c111abd8c462a85a6ba
tines/tines-command-runner:7dc418f7_v37_2_2 sha256:9fff85ee721497aa4ace254a25f8872d8ed7563ced60fdb1d3d1780aed0dd933
tines/tines-nginx:7dc418f7_v37_2_2 sha256:54cdf092145d3ebe9f68d84ddc104abf7025513d60d726bea7f5b3dadc4806a3
```

- Removes index idx_team_case_notes_composite_search
- Adds database fallback for reading managed OAuth credential client id & secrets 

### December 2nd, 2025 - 37.2.1

```
Image digests for build 53c5e809_v37_2_1:
tines/tines-app:53c5e809_v37_2_1 sha256:1e7f8c0991ba531e2fbc1b0a93eb959c6d0751d07bef84c1518cb8661c144e85
tines/tines-command-runner:53c5e809_v37_2_1 sha256:eb90c5e33e85286d1138ce70e2cc4db6ed0fa44f46fe6c76b4c56fb93c30890c
tines/tines-nginx:53c5e809_v37_2_1 sha256:ee96970e0879c2b580d60b9e7c5d8a953011e06e719695ae2d3bfb7179316a67
```

- Fixes an issue in **tines-app-fips** where it was not possible to run `update-ca-certificates` to mount custom certificates

### December 2nd, 2025 - 37.2.0

```
Image digests for build bb3a75b7_v37_2_0
tines/tines-app:bb3a75b7_v37_2_0 sha256:a20dd37c2f6ca5d694ef145780e60302d89aaa5c8d8c85e0aa84dde7223d6a85
tines/tines-command-runner:bb3a75b7_v37_2_0 sha256:f6688009c2e7770ab44501388f323922fd4699b905660330fe04e51f05d49c70
tines/tines-nginx:bb3a75b7_v37_2_0 sha256:36dc55e46956a8e215c7c15bff860e46d0fbf3848eefa1804b539d2cf9f80d51
```

- [Dashboards record table](https://www.tines.com/whats-new/dashboards-record-table)
- [Rich text callouts in Cases](https://www.tines.com/whats-new/rich-text-callouts-in-cases)
- [Improved case search and saved views](https://www.tines.com/whats-new/improved-case-search-experience)

### November 25th, 2025 - 37.1.0

```
Image digests for build 41917b44_v37_1_0
tines/tines-app:41917b44_v37_1_0 sha256:85e077fcbc2c6d87e1896909532acb558cd9764c2ae00aea8da1930e6d0ad073
tines/tines-command-runner:41917b44_v37_1_0 sha256:3da98eb9e348bd51a7aa1be2975866ef36a6dd525c5bdce32390dd248239b26b
tines/tines-nginx:41917b44_v37_1_0 sha256:80acc6936a68922fba80deb6e68f982a9adaae6b4f1fa8717b001bee953a5725
```

- `Dockerfile` contents were updated.
- [Send to Story filter by team](https://www.tines.com/whats-new/send-to-story-filter-by-team)
- [Record limits increase: 50 fields, 100 validation options](https://www.tines.com/whats-new/records-limits-increase)
- [Dynamic "Use tunnel" field](https://www.tines.com/whats-new/dynamic-use-tunnel-field)
- [Testing story runs](https://www.tines.com/whats-new/testing-story-runs)
- [Team ID header included in HTTP requests](https://www.tines.com/whats-new/team-id-header-included-in-http-requests)
- [Using tools from MCP servers in AI Agent actions](https://www.tines.com/whats-new/using-tools-from-mcp-servers-in-ai-agent-actions)
- [AI Agent actions in Slack](https://www.tines.com/whats-new/ai-agent-actions-in-slack)
- [Filter case parameters by specific time range](https://www.tines.com/whats-new/filter-case-parameters-by-specific-time-range)
- [Upload files into AI Agent chat pages](https://www.tines.com/whats-new/upload-files-into-ai-agent-chat-pages)
- [Cases toolbar & shortcuts](https://www.tines.com/whats-new/cases-toolbar)

### November 18th, 2025 - 37.0.0

```
Image digests for build c98662c3_v37_0_0
tines/tines-app:c98662c3_v37_0_0 sha256:bb370f47de906ba027fbb7994d9fe7c13ff3945c52732cc13b9726052c22a0c4
tines/tines-command-runner:c98662c3_v37_0_0 sha256:242d1912ae7cb68c86567e00235a5afdad734ce018d4116f8e98eebebb1a6711
tines/tines-nginx:c98662c3_v37_0_0 sha256:e811b702a62400427488ec8053f5384f9882adcf4eb74f6b166516a772e9ad0a
```

- Fixed database sequences for high-volume tables (`agent_logs, events, agents`, etc.) to support full bigint range (~9 quintillion records). Prevents production issues when reaching 2.1B+ records. Migration runs automatically on upgrade.
- Activates built in Postgres btree_gin extension for future usage.
- Migrates **tines-command-runner** and **tines-command-runner-fips** images to distroless
- [Filter case parameters by specific time range](https://www.tines.com/whats-new/filter-case-parameters-by-specific-time-range/)
- [Toggle event payloads order in the events console](https://www.tines.com/whats-new/toggle-event-payloads-order-in-the-events-console/)

### February 17th, 2025 - 36.2.3

```
Image digests for build aa4f29ac_v36_2_3
tines/tines-app:aa4f29ac_v36_2_3 sha256:4baf50c2f8e3b2670fa64380458e0fabcb0797b841fb7774947854b80f8613d9
tines/tines-command-runner:aa4f29ac_v36_2_3 sha256:2c04efca5d2bd8a8503ac1c434aaaaa4d79ab271bef8138a639fc174b18c4119
tines/tines-nginx:aa4f29ac_v36_2_3 sha256:1f40463a96cb3f8eaf83d817a91b7a6dee620e4bb4e1637b383da31f054d074a
```

- Security fixes

### ~~December 12rd, 2025 - 36.2.2~~ Deprecated in favour of 36.2.3

```
Image digests for build b32650de_v36_2_2
tines/tines-app:b32650de_v36_2_2 sha256:b7e88f8ed5f7c527bb106e641e131248c76726bad77e77db7ae57cbc0116a652
tines/tines-command-runner:b32650de_v36_2_2 sha256:5f72272d46f87313d0802e033275930af434919b8e5dff2e3778548f3a11d640
tines/tines-nginx:b32650de_v36_2_2 sha256:9b7d4de0ced374d831f8bb86596a713bb9f3c80a63845b60d93f2e3050eb1d64
```

- Fixes default runtime for Run Script actions

### December 3rd, 2025 - 36.2.1

```
Image digests for build ec4d017a_v36_2_1:
tines/tines-app:ec4d017a_v36_2_1 sha256:8617d760016742dab792e7a137d4128f6c0b2af3163b04bb364dd77a8e66a3dc
tines/tines-command-runner:ec4d017a_v36_2_1 sha256:17088570baebd00122e6b05494f3f986bd557950ee1b1a5d66f6e3fc84638de0
tines/tines-nginx:ec4d017a_v36_2_1 sha256:c0d7e68f5dd40f43f8ef745faae373eb433d97d032668bb7f1614274efa4e58a
```

- Fixes an issue in **tines-app-fips** where it was not possible to run `update-ca-certificates` to mount custom certificates

### November 11th, 2025 - 36.2.0

```
Image digests for build 3db3462e_v36_2_0
tines/tines-app:3db3462e_v36_2_0 sha256:7cbd0865cc52286ac9096dcfefb4ef82b133b3d0ca7688ffb7a367710d6cf265
tines/tines-command-runner:3db3462e_v36_2_0 sha256:c51dd965f6b69f8b6f3e1de9d1ef973301c4a071612209e8faf48b008682cb23
tines/tines-nginx:3db3462e_v36_2_0 sha256:969390b0fa8b038d97907f9b6848cb7ed0b843a5bea89b6b8f129a243ecefc06
```



- [Text change highlighting](https://www.tines.com/whats-new/text-change-highlighting/)
- [Using tools from MCP servers in AI Agent actions](https://www.tines.com/whats-new/using-tools-from-mcp-servers-in-ai-agent-actions/)

### November 4th, 2025 - 36.1.0

```
Image digests for build cf9f8b24_v36_1_0
tines/tines-app:cf9f8b24_v36_1_0 sha256:d80f4ea31594025ff6b94a5867b34896113e8be5b03858aaf8b14b23f8833ebe
tines/tines-command-runner:cf9f8b24_v36_1_0 sha256:1474583859cb3b0da272994c1eccdd1cd7a2c36f7784c59714ffb79a225a75b0
tines/tines-nginx:cf9f8b24_v36_1_0 sha256:6162ea66eee90ace8d150760fb4e17e577534805a71894726f97d846f0b2cec7
```

- Upgrades ruby to 3.4.5
- **tines-command-runner-fips**: Fixes an SSL error encountered when executing a Run Script action with the `requests` python library
- [October template update](https://www.tines.com/whats-new/october-template-update)
- [October connect flow update](https://www.tines.com/whats-new/october-connect-flow-update)
- [Update Record Type API](https://www.tines.com/whats-new/update-record-types-via-the-api)
- [Manually create a record within a Record Type](https://www.tines.com/whats-new/manually-create-records)
- [Branding controls for Pages](https://www.tines.com/whats-new/branding-controls-for-pages)
- [Configure headers for the AWS Bedrock AI provider](https://www.tines.com/whats-new/configure-headers-for-the-aws-bedrock-ai-provider)

### October 30th, 2025 - 35.0.2

```
tines/tines-app:410f1187_v35_0_2 sha256:c2d4187be3380e1b2aed8097e16426b51a708586a5e977e4d82d56dcd58eff1c
tines/tines-command-runner:410f1187_v35_0_2 sha256:15f404689d8773148a4b9ecf9b0ef9c2e20f963255d8e4d23a85cb3d12946325
tines/tines-nginx:410f1187_v35_0_2 sha256:e07708978b962ed97a0ff31b5fca401b2c4c0ce78fc79d77eda6f6c5852c9df9
```

- Fixes a minor bug around database connection handling

### October 29th, 2025 - 35.0.1

```
Image digests for build 5b81af32_v35_0_1:
tines/tines-app:5b81af32_v35_0_1 sha256:476936ca8f6b48ca3b31f9481734faa0c46066d0de05e6ccf1cfdd105ac2cfcd
tines/tines-command-runner:5b81af32_v35_0_1 sha256:0c0443f59e12de4e841e6b828bfcb9272d8402a4b7df028910d27516d228c4c3
tines/tines-nginx:5b81af32_v35_0_1 sha256:5cd86a2ca06d5e098ed5c7a10aadace42f7de8f3076f932fbe5ce5411e06c599
```

- Fixes a minor bug where feature flags were being checked unnecessarily.

### October 28th, 2025 - 36.0.0

```
Image digests for build 90db0680_v36_0_0:
tines/tines-app:90db0680_v36_0_0 sha256:25f5437c1e9448f2a864d329ee27ef829781cade6cdb37ef0d4d018040cf3fed
tines/tines-command-runner:90db0680_v36_0_0 sha256:012e70e34bdf883611c8bd57e36037d0225a883e4b280fa266c5c2f289ec70ca
tines/tines-nginx:90db0680_v36_0_0 sha256:8c20300e1a3ee87475304fa4a479e37d306fae35d5ad35d87236aec6f09f3817
```

Note: It is safe to upgrade from v34 directly to v36. This also marks a significant change to the tines-app-fips image moving to a new distroless base to improve security and maintainability.

- [AI Agent action - JSON schema builder](https://www.tines.com/whats-new/ai-action-json-schema-builder)
- [Authentication settings redesign](https://www.tines.com/whats-new/authentication-settings-redesign)
- [Configure headers and base URL for the Anthropic AI provider](https://www.tines.com/whats-new/configure-headers-and-base-url-for-the-anthropic-ai-provider)
- [Send to story input action now includes metadata about the calling Story](https://www.tines.com/whats-new/send-to-story-input-action-now-includes-metadata-about-the-calling-story)

### October 20th, 2025 - 35.0.0

```
Image digests for build f55c064f_v35_0_0:
tines/tines-app:f55c064f_v35_0_0 sha256:c6ec74e378aceae754c39697b20eec0955def06512e19ab629bf50e324abdd34
tines/tines-command-runner:f55c064f_v35_0_0 sha256:ecbafa2a73d5be7a1fc802d30b98ef4191b8ef58f066210cc0dd9e15abd7bdb0
tines/tines-nginx:f55c064f_v35_0_0 sha256:f0c71f21c409000c796f76399c2979c21427b3fb6746eca03848ddd3b7b62afe
```

- No significant changes in this update - it's a major release in line with our regular cadence of 8 weeks.

### January 29th, 2026 - 34.9.1

```
Image digests for build fa431dc9_v34_9_1:
tines/tines-app:fa431dc9_v34_9_1 sha256:551c773fb838183b534e6a5acf329f18e6f087cb7bbef6511807377d52bcc939
tines/tines-command-runner:fa431dc9_v34_9_1 sha256:908967f562b1d6b0898d1c97049d6eb37c76a2b441df71888442ed2c435c2c2b
tines/tines-nginx:fa431dc9_v34_9_1 sha256:093cfae49989f94568e66e10e0317f05d340d64bbeee1ef0ad077a169e706dc3
```

- Fixes a bug when upgrading from versions related to database permissions.

### October 14th, 2025 - 34.9.0

```
Image digests for build 4d303a1e_v34_9_0:
tines/tines-app:4d303a1e_v34_9_0 sha256:6bd8e410159b9dc87dde4bfc251baaa233bf1a9e03aedde32168972d60ee79fd
tines/tines-command-runner:4d303a1e_v34_9_0 sha256:91c8ab2563d1f7f6d9342d8da3d12089b2d95436e2767606b315cb36b9f0f689
tines/tines-nginx:4d303a1e_v34_9_0 sha256:1700389fc2906898642f8bf9d077e7fc26fd8b4b82cb0d4dec6e0808246f2ef6
```

This change adds a number of binaries to our docker image, as well as some paths used for custom certificate authorities.

### October 13th, 2025 - 34.8.0

```
Image digests for build 76340244_v34_8_0:
tines/tines-app:76340244_v34_8_0 sha256:2b27cfcc9b775187d3ea99f1745d3383971ffcbee64537171d6c0203ceb89a02
tines/tines-command-runner:76340244_v34_8_0 sha256:9877f52b381699e51f7e92bf399430f2a7d04c8a60fe6605dba323ed5db90f43
tines/tines-nginx:76340244_v34_8_0 sha256:18693254351dc20cf7347bf25ef63cbabdf0ba03970f1422447d5f8b3cd71fe2
```

- [Upload files into AI Agent chat pages](https://www.tines.com/whats-new/upload-files-into-ai-agent-chat-pages/)

### October 8th, 2025 - 34.7.0

```
Image digests for build 56c52933_v34_7_0:
tines/tines-app:56c52933_v34_7_0 sha256:cf7fbd7671b3d1ebc76a894ed46af0ae4717b2affcb023a623a2e31af42b7990
tines/tines-command-runner:56c52933_v34_7_0 sha256:3ba15ef79e4bd606f84fdfdf83ade999485396f9b000b79dff6045161872bb9e
tines/tines-nginx:56c52933_v34_7_0 sha256:fdf086c6fcf8d80fba624b643077d30ba7ee4549a544dcaf61495ced3e9e6bad
```

- [Jump from a story run straight to its route in the diagram](https://www.tines.com/whats-new/jump-from-a-story-run-straight-to-its-route-in-the-diagram/)
- [Live activity for pending action runs in stories](https://www.tines.com/whats-new/adding-visibility-to-story-performance/)
- [Restrict access to Run Script actions and Event Transform actions in automatic mode](https://www.tines.com/whats-new/restrict-access-to-run-script-actions-and-event-transform-actions-in-automatic-mo/)
- [Export OpenTelemetry traces for self-hosted installations](https://www.tines.com/docs/self-hosting/additional-configuration/exporting-opentelemetry-traces/)

### October 6th, 2025 - 34.6.0

```
Image digests for build 9aabbfee_v34_6_0
tines/tines-app:9aabbfee_v34_6_0 sha256:db0a50670cb2259443070501ee6f89b51d1f9a13e7d0e15554d6f747b82f53b8
tines/tines-command-runner:9aabbfee_v34_6_0 sha256:e579603defc819950a610b35c4add4ff822fef74eecaa06eb5d656bd9997b8a0
tines/tines-nginx:9aabbfee_v34_6_0 sha256:6c6a29c932696c6e51a75e991d6ff7bdde51aefbd06c2bb1cb0eda33bbd41009
```

- [Manage page themes visually](https://www.tines.com/whats-new/manage-page-themes-visually/)
- [Assignable tasks in Cases](https://www.tines.com/whats-new/assignable-tasks/)
- [Configurable Send-to-Story tool timeouts](https://www.tines.com/whats-new/configurable-send-to-story-tool-timeouts/)

### September 29th, 2025 - 34.5.0

```
Image digests for build 60a9ccef_v34_5_0
tines/tines-app:60a9ccef_v34_5_0 sha256:21d24c485392f179b82f060dd2c61490cc09db1cf9a3c0968b5f694c5fc88e7b
tines/tines-command-runner:60a9ccef_v34_5_0 sha256:6bf20f4951dad3c1f90472b50a2d3487460740b88f7805037eb4f6f1b850c656
tines/tines-nginx:60a9ccef_v34_5_0 sha256:6a6185824ddd8e9e4d19896b780b41636a8e66e8ed3515b692347501c8538e94
```

- [Connector design refresh](https://www.tines.com/whats-new/connector-design-refresh/)
- [Drag and drop actions onto AI actions as tools](https://www.tines.com/whats-new/drag-and-drop-actions-onto-ai-agent-actions-as-tools/)
- [Team event limits](https://www.tines.com/whats-new/team-event-limits/)
- [Saved story runs redesigned](https://www.tines.com/whats-new/saved-story-runs-redesigned/)

### September 24th, 2025 - 34.4.1

```
Image digests for build 290e5111_v34_4_1
tines/tines-app:290e5111_v34_4_1 sha256:01be393fdfb5c67c8304f67dc1137154d136c93440bc221a609271adfbcee4c7
tines/tines-command-runner:290e5111_v34_4_1 sha256:57b1c0177534691feac5eeb996d6cd1869f79289837e8086f53d790c4e06ebf9
tines/tines-nginx:290e5111_v34_4_1 sha256:e47bc979eb74e05a9956a80f2fcfad37f76cc47a842811b38e6908da7f0f1114
```

- Resolves a bug **only contained** in release **34.4.0** which prevented installs with > 1 million records from creating new records. 

### ~~September 22nd, 2025 - 34.4.0  ~~ Deprecated in favour of 34.4.1

```
Image digests for build 7bd6769d_v34_4_0
tines/tines-app:7bd6769d_v34_4_0 sha256:5e7ee935894e1bd9d0099ea813ed21dc286cb4cae712705e0d9bee35ad8be1f8
tines/tines-command-runner:7bd6769d_v34_4_0 sha256:b430aa42fc51779197a4f80352b237f2958473c5181c084e60446d3afdd1c3ec
tines/tines-nginx:7bd6769d_v34_4_0 sha256:b0de52c524948cc49b0393a2987f356c7100a57e076c057235c00b9bf72e8db9
```

- [Enable multiple AI providers](https://www.tines.com/whats-new/enable-multiple-ai-providers)
- Multiple bug fixes including a race condition that affected JIT provisioning when Enhanced JIT was enabled.
- Security fixes

### September 22nd, 2025 - 33.0.3

```bash
Image digests for build 06ab1ab5_v33_0_3:
tines/tines-app: sha256:992019be5e59c02e87af731aab95c13fcde3d601e14de34c12aa745b6d4067c6
tines/tines-command-runner: sha256:8979d77662675a98fc5d85f1ee0d8c726dd6f6fc452840ef3ad4a7d02fbd0f67
tines/tines-nginx: sha256:24454cd599a54ffb68dfc473b920dbb44cf10443f9a246037df8c69ec72477c3
```

- Security fixes

### September 17th, 2025 - 34.3.1

```bash
Image digests for build fbf20ee8_v34_3_1:
tines/tines-app: sha256:2e9f2d76bb2283d5514fff1952d13d957fa8733db6937ed87bd79078cd61f4ad
tines/tines-command-runner: sha256:3d24219a813f37ce5016e0c8216bd0822730aa750335eda1b85b02b00b2b0b14
tines/tines-nginx: sha256:6aaf58bab14cced8fbf2b9fbe319ce107a26ecf6aa6c4181211473fc3f2e092d
```

- Includes various performance improvements (paritcularly around shared credentials, resources & stories)
- Adds new optional `include_referencing_action_ids` parameter to the resources API. This will exclude `referencing_action_ids` from the response when passed as `false`

### September 15th, 2025, 34.3.0

Note: We've made an update to our release process to have a more detailed Docker Image Manifest. If you pull images on a non-x86_64 system you may need to add the flag `--platform linux/amd64`.

```
Image digests for build 06ab1ab5_v33_0_3:
tines/tines-app: sha256:2291d099c6f9f7bcc34559f9931ff99b51002f018c64ca0b01b487758e42cb0f
tines/tines-command-runner: sha256:a1793a6d4e4f7a4b0cfbae2cc96abbfa43f336fca1198856c49d6c89c560f3b0
tines/tines-nginx: sha256:1836df53a444db6f2d35c6929a44fa10467d2d67cd3ed557971d18b01f4d62e9
```

- [Duplicate and reorder match rules](https://www.tines.com/whats-new/duplicate-and-reorder-match-rules)
- [Audit log export to S3](https://www.tines.com/whats-new/audit-log-export-to-s3)
- [Cases toolbar & shortcuts](https://www.tines.com/whats-new/cases-toolbar)
- [Bulk export selected cases](https://www.tines.com/whats-new/bulk-export-selected-cases)
- [Clearer Storyboard views for story runs](https://www.tines.com/whats-new/clearer-storyboard-views-for-story-runs)
- [Cases improvements](https://www.tines.com/whats-new/cases-improvements-sep-2025)
- [Paste actions that can be tools onto the AI Agent action](https://www.tines.com/whats-new/paste-actions-that-can-be-tools-onto-the-ai-agent-action)
- [Records and Dashboard improvements](https://www.tines.com/whats-new/records-and-dashboard-improvements-sep-2025)
- [Dashboards conditional color style](https://www.tines.com/whats-new/dashboards-conditional-color-style)
- [Multiple dashboard snapshots](https://www.tines.com/whats-new/multiple-dashboard-snapshots)
- [Bulk link cases to a specific case](https://www.tines.com/whats-new/bulk-link-cases)
- [Get contextual help where you need it](https://www.tines.com/whats-new/get-contextual-help-where-you-need-it)

### September 12th, 2025 - 34.2.2

```bash
Image digests for build d359aa94_v34_2_2:
tines/tines-app: sha256:b853fca7c5c55cfc75e90e0e7b5bc191a91e18ccd574c7f6dd4a5990d194ff00
tines/tines-command-runner: sha256:705fcb42227e5407a6b0f5d05844e559a64a803a20aa197f230601ac01000802
tines/tines-nginx: sha256:3c3b2dc7a1484de62e8773255073a15facda80c3504ff02fc9d4da3abc230b2b
```

- Contains a bug fix and backfill migrations to resolve an issue with time-based implode values being stuck in memory

### September 10th, 2025 - 34.2.1

```bash
Image digests for build d359aa94_v34_2_1:
tines/tines-app: sha256:f3f2daedc62ef029c88e07790a271cb00a3d13dd9a427d2adf51e29661943929
tines/tines-command-runner: sha256:d3d641084d4777a21a62e9ae48c7ac4b892eb3811b05571019ab605da5d85b31
tines/tines-nginx: sha256:3c3b2dc7a1484de62e8773255073a15facda80c3504ff02fc9d4da3abc230b2b
```

- Contains a bug fix to logging inside container when JSON based logging is enabled

### September 8th, 2025 - 34.2.0

```
Image digests for build 96fac770_v34_2_0:
tines/tines-app: sha256:69ac89a73a194797b923de65233136f9e6a72571269267b3ba1d4215f0c9da5d
tines/tines-command-runner: sha256:247e585a1c434dd0a9abb5f42f3d0e21f32415bed837d1ddbdf8277bb757bdc3
tines/tines-nginx: sha256:3c3b2dc7a1484de62e8773255073a15facda80c3504ff02fc9d4da3abc230b2b
```

- [Cases HTML block](https://www.tines.com/whats-new/cases-html-block)
- [Reorder case statuses](https://www.tines.com/whats-new/reorder-case-statuses)

### September 5th, 2025 - 34.1.4

```bash
Image digests for build 68e9dc13_v34_1_4:
tines/tines-app: sha256:355e8d73f6b2ccef8c686f3aa0226adf6bfefe55593c199fd65c894948648140
tines/tines-command-runner: sha256:9505a447a79b6e76fe54bc8a54ec6453c37a782603e27b33b112a1c806e3f323
tines/tines-nginx: sha256:490ee5f93d55f1af78d0354423189623be94b0964f7fc6a5e8d23ab658097e34
```

This release is essentially identical to 34.1.2, concluding the test of our release process changes from 34.1.3. It is available in all registries.

### September 5th, 2025 - 34.1.3

```bash
Image digests for build d150be77_v34_1_3:
tines/tines-app: sha256:b1d632b60c875be206bfbc04062782c533e1ea71220cd7b4e98b077e28368011
tines/tines-command-runner: sha256:10924886d6ca2ed0776021aa5444e7bb2339d6078934c3ef6d31b7c7457e7005
tines/tines-nginx: sha256:490ee5f93d55f1af78d0354423189623be94b0964f7fc6a5e8d23ab658097e34
```

This release was a test of our internal release systems. It is not available on `registry.tines.com`, please prefer `34.1.2` or 34.1.4.

### ~~September 5th, 2025 - 33.0.2~~ Deprecated

```bash
Image digests for build 4e09fa41_v33_0_2:
tines/tines-app: sha256:3878822bfe81a3c01900142c37f2e026b4352f154726c8db49e3cad994bae424
tines/tines-command-runner: sha256:0c330c69b885c932c5ca5bc3a66dfc08ff536c64f63c92a13c5d4eac510fe29f
tines/tines-nginx: sha256:115c3a3e2cd0f830c3d29871658ba664487ab11f50c44b8be715bd01c1bfd22c
```

There are no changes compared to `33.0.1`.

### September 5th, 2025 - 34.1.2

```bash
build-sha f1134c73_v34_1_2
tines/tines-app: sha256:64b01e7b1c93a0dbfe2867865ec1a111a1d170847c546c5a31aee50cfd0afeae
tines/tines-command-runner: sha256:088815af5aa7a320475e4a7f679e26db7a12b8c55415c1e257aa8c84d64e8113
tines/tines-nginx: sha256:490ee5f93d55f1af78d0354423189623be94b0964f7fc6a5e8d23ab658097e34
```

- Fixed an issue with pages content not showing up in cases until the user clicks away

### September 2nd, 2025 - 34.1.1

```bash
build-sha 676ebf29_v34_1_1
tines/tines-app: sha256:12373aef2affef726c3b940a6f1870b0c7fa78d396e8c2dc758ce974529c543a
tines/tines-command-runner: sha256:da4b47f0606db0db9a307862064bae7e49a11ce425ab7fc6e4577a4d5db446c5
tines/tines-nginx: sha256:490ee5f93d55f1af78d0354423189623be94b0964f7fc6a5e8d23ab658097e34
```

- Fixed an issue with story syncing settings



### September 1st, 2025 - 34.1.0

```bash
build-sha 397b06cd_v34_1_0
tines/tines-app: sha256:b4f4f57bbb3e7543d2ecee5936a6006785fcc3e0f7411cc01d17001faeb50f7d
tines/tines-command-runner: sha256:a83cb89e2ca337ff37eb594cd7c224a2d51eac20f7bfb56280df7c5116083bce
tines/tines-nginx: sha256:490ee5f93d55f1af78d0354423189623be94b0964f7fc6a5e8d23ab658097e34
```

> **NOTE:** Upgrades Tines PostgreSQL images from 14.17 -> 14.19 in order to guard against an pg\_dump vulnerability outlined in [CVE-2025-8715](https://nvd.nist.gov/vuln/detail/CVE-2025-8715).

- [Pre‑populate Workbench chat via URL](https://www.tines.com/whats-new/pre-populate-workbench-chat-via-url)
- [Auto generate formulas](https://www.tines.com/whats-new/auto-generate-formulas)
- [Idle timeout support in chat mode](https://www.tines.com/whats-new/idle-timeout-support-in-chat-mode)
- [AI Agent action monitoring](https://www.tines.com/whats-new/ai-agent-action-monitoring)
- [Median aggregation function for dashboards](https://www.tines.com/whats-new/median-aggregation-function-for-dashboards)
- [Auto generate change request descriptions](https://www.tines.com/whats-new/auto-generate-change-request-descriptions)

### August 26th, 2025 - 34.0.0

```
build_sha 00d17b47_v34_0_0
tines/tines-app: sha256:b4737a9a1d8e70db1ad30fc0c4aacf64c6c8e2fcb7804c950f3c0b28502c1ec0
tines/tines-command-runner: sha256:7395753439f2fdc99c5bab501c0bd93f44a76b0a7d70ec67406c0c507537f8e8
tines/tines-nginx: sha256:25a680b00fe09b607b8194027880413524867749922debf53b8b31f6e68d0ff8
```

- Deprecation of Docker Content Trust in favor of cosign - see [docs](https://www.tines.com/docs/self-hosting/general-upgrade-recommendations/verifying-tines-images/) on verifying Tines images
- From version 31.2.0 onwards we recommend using Redis 7, see our [docs](https://www.tines.com/docs/self-hosting/general-upgrade-recommendations/upgrading-redis/) for instructions on upgrading Redis and this [Redis blog post](https://redis.io/blog/introducing-redis-7/) on some of the benefits of Redis 7
- Moving to the distroless model for our images
- [Select AI model in Workbench](https://www.tines.com/whats-new/select-ai-model-in-workbench/)
- [New `JWT_DECODE` function](https://www.tines.com/whats-new/new-jwt-decode-function/)
- [Case list columns included in saved view](https://www.tines.com/whats-new/case-list-columns-included-in-saved-view/)
- [Add API support for saving story runs](https://www.tines.com/whats-new/add-api-support-for-saving-story-runs/)
- [Accept all incoming changes](https://www.tines.com/whats-new/accept-all-incoming-changes/)
- [Code Analyst available in AI Agent actions](https://www.tines.com/whats-new/code-analyst-available-in-ai-agent-actions/)
- [Action metadata includes pending_duration](https://www.tines.com/whats-new/action-metadata-includes-pending-duration/)
- [New `JWK_TO_PEM` and `PEM_TO_JWK` functions](https://www.tines.com/whats-new/new-jwk-to-pem-and-pem-to-jwk-functions/)
- [Option to always open cases in a new tab](https://www.tines.com/whats-new/option-to-always-open-cases-in-a-new-tab/)

### August 20th, 2025 - 33.0.1

```
build_sha c6fff471_v33_0_1
tines/tines-app: sha256:26c145858b98e7085964e8178f35e772746dc01b9cc2c9ca10057c9318dc1f85
tines/tines-command-runner: sha256:c9176b992467f7f2c0614060cb19ae2757580695ba896b93c11a82657e2b47d7
tines/tines-nginx: sha256:3f2cbb151c2006328d05b9c8dd91a14fe6054d97f3790b1e429a9381b058a608
```

- Fixed an issue that required customers using a custom `DATABASE_PORT`  to also set `DATABASE_READONLY_PORT`. `DATABASE_PORT` is the only required variable as part of this release.

### August 20th, 2025 - 32.7.3

- Fixed an issue that required customers using a custom `DATABASE_PORT`  to also set `DATABASE_READONLY_PORT`. `DATABASE_PORT` is the only required variable as part of this release.

```
build_sha 719b1e2b_v32_7_3
tines/tines-app: sha256:fca24cdba0eee76b40ed59731f901fb44bdcb52eb242f50a08a7b47a6d77369f
tines/tines-command-runner: sha256:1ea2baf5018e72f73981abeafc77715001efe27bb63ec17d7e746da4eecd5273
tines/tines-nginx: sha256:afd245ed90c49803ee95b1095f25b5e8affc31ca4d2fb3dd9206ca56b0aff5e5
```

### ~~August 18th, 2025 - 33.0.0   ~~  Deprecated in favour of 33.0.1

```

build_sha ec34060a_v33_0_0
tines/tines-app: sha256:33e37380cc75faa9505bacccdb01e73a1d8d7ded8b1227ba9c26e043817e2760
tines/tines-command-runner: sha256:2c4f9f21d12c0d931ce3b97a723dc52285e93f68f19f85ff8f43ba6f997f1098
tines/tines-nginx: sha256:8b904bb831758931a992e1e32c5448d2d4fc0f4ae41f15081c3ee90f30930ff7
```

- [Dashboards toolbar](https://www.tines.com/whats-new/dashboards-toolbar)

- [Share team objects with members' personal teams](https://www.tines.com/whats-new/share-team-objects-with-members-personal-teams)

- [Enhanced AI usage reporting](https://www.tines.com/whats-new/enhanced-ai-usage-reporting)

- [Credential expiry notifications](https://www.tines.com/whats-new/credential-expiry-notifications)

- [Action type added to webhook monitoring payload](https://www.tines.com/whats-new/action-type-added-to-webhook-monitoring-payload)

- [Update to case notification settings](https://www.tines.com/whats-new/case-notification-settings-update)

- [Embedded pages for cases](https://www.tines.com/whats-new/embedded-pages-for-cases)



### August 15th, 2025 - 30.3.7

- Fixed an issue with integrations syncing.

```
build_sha a098a9cc_v30_3_7
tines/tines-app: sha256:791ec599e8205309c57514a6dfcc1339e091bafb02c16581e946c4580244a913
tines/tines-command-runner: sha256:0777261155a693a924c59ffe17e6745e55b2c5ef81f5878b5ba1424305ef5f81
tines/tines-nginx: sha256:c862c92eb411f71dde7a5df902bb3d3cfe05ddb2d180723b36fe6776b6ce2fc9
```

### ~~August 15th, 2025 - 32.7.2. ~~ Deprecated in favour of 32.7.3

- Fixed an issue where if `DATABASE_PORT` was not 5432 it was not respected.

```
build_sha 22028161_v32_7_2
tines/tines-app: sha256:43aecd2b6263a26fa227667f31dcdfa6dd17aebc563e9135484215df423ca4b4
tines/tines-command-runner: sha256:0777261155a693a924c59ffe17e6745e55b2c5ef81f5878b5ba1424305ef5f81
tines/tines-nginx: sha256:042b9a602d7b39e590b500c937a736b16b2d6af1c75bd4c923846fc2d9ff6878
```

### August 15th, 2025 - 32.6.1

- Fixed an issue where if `DATABASE_PORT` was not 5432 it was not respected.

```
ddcea6eb_v32_6_1
tines/tines-app: sha256:beff7c97146bf3d9f567c6fd13a4208f9f447ac40ae2d2e79bf275d30d9cb48f
tines/tines-command-runner: sha256:2fa499bb7874636dcbdbc00d3e0746a2f8df6673107dbc77e939d4b5b8ed0108
tines/tines-nginx: sha256:d8217ff96b6e442935b5ac3c106e53e4729c17b62b0cece69abae0d5c2c0c4d3
```

### ~~August 14th, 2025 - 32.7.1 ~~ **[DEPRECATED IN FAVOR OF 32.7.2]**

```
build_sha 3cefea98_v32_7_1
tines/tines-app: sha256:fac0ccbf782bab3aa53b794e2c8006a83715968ba562cc232d36fd282ef42cfb
tines/tines-command-runner: sha256:c1c7d78f5bfec8df77411ee67f55c80cc9e62fa76bc6b53c1fd91f5ec148f3db
tines/tines-nginx: sha256:042b9a602d7b39e590b500c937a736b16b2d6af1c75bd4c923846fc2d9ff6878
```

### ~~August 11th, 2025 - 32.7.0 ~~ **[DEPRECATED IN FAVOR OF 32.7.2]**

```
build_sha 82315034_v32_7_0
tines/tines-app: sha256:52a79f0232ad62a0ca1b552c64ad50ef01809b68c60852be27abf215b78b8a27
tines/tines-command-runner: sha256:ff02cae5e2904177eaa1e8cb10ea50fcd61fb78892142f2945e913054446db7e
tines/tines-nginx: sha256:92ed988462ec4e8877cf0c590310e164a7a98b03cca871a6fb08a6641d95a148
```

- [Use your own AWS Bedrock in Tines cloud](https://www.tines.com/whats-new/use-your-own-aws-bedrock-in-tines-cloud)
- [Case comments rich text improvements](https://www.tines.com/whats-new/case-comments-rich-text-improvements)
- [Close submitted pages immediately](https://www.tines.com/whats-new/close-submitted-pages-immediately)
- [Building MCP servers in Tines](https://www.tines.com/whats-new/building-mcp-servers-on-tines)
- [Embedded pages in collections](https://www.tines.com/whats-new/embedded-pages-in-collections)
- [Action duration metadata is available](https://www.tines.com/whats-new/action-duration-metadata/)

### ~~August 4th, 2025 - 32.6.0~~ **[DEPRECATED IN FAVOR OF 32.6.1]**

```
build_sha f58a27ec_v32_6_0
tines/tines-app: sha256:8e89078b994de869f14d8922dc803e636b2ae9eb3cd0de288c7eb7d17bcdf80e
tines/tines-command-runner: sha256:47758a1cab978ed2b95d353bc4edc02dcb30e98530f1bb4a912e30b61f939aa0
tines/tines-nginx: sha256:cda97cd688c96a38eabd3fa31cdea942070f95029eed03cd907c30552532b5d5
```

- [Pre-filter events on HTTP Request and Event Transformation Actions](https://www.tines.com/whats-new/pre-filter-events-on-http-request-and-event-transformation-actions/)
- [Granular dashboard permissions](https://www.tines.com/whats-new/granular-dashboard-permissions/)
- [Cases information card](https://www.tines.com/whats-new/cases-information-card/)
- [Improved case action configuration](https://www.tines.com/whats-new/improved-case-action-configuration/)
- [Webhooks support Tines API keys](https://www.tines.com/whats-new/webhooks-now-support-api-keys/)
- [Case template creation 2.0](https://www.tines.com/whats-new/case-creation-templates-refresh/)
- [User-driven looped elements for Pages](https://www.tines.com/whats-new/user-driven-looped-page-elements/)
- [Unframed pages](https://www.tines.com/whats-new/boundless-pages/)
- [AI Agent duration metadata](https://www.tines.com/whats-new/ai-agent-duration-metadata/)
- Bug fix for run script in airgapped environments

### July 28th, 2025 - 32.5.0

```
build_sha bb0d2b42_v32_5_0
tines/tines-app: sha256:82a89e398d8e3096c1576a3ab8b9b8f574fe9b00372f9c3712c63b056e4b6f72
tines/tines-command-runner: sha256:703f9370e74ce0dbf93c7783a9d75b22c2d20de62e01e4a19ba9d8a796141400
tines/tines-nginx: sha256:c4f3b011362cba1fb04b77f8eaf5b91ee23e4ae09b4305b5b4463cf9a40f3343
```

- ** **[**Recent stories**](https://www.tines.com/whats-new/recent-stories)
- [**Capture the raw outgoing message in the Send Email action**](https://www.tines.com/whats-new/capture-the-raw-outgoing-message-in-the-send-email-action)
- [**Cases file size limit increased to 60 MB**](https://www.tines.com/whats-new/cases-file-size-60mb)
- [**Share controls for Pages**](https://www.tines.com/whats-new/share-controls-for-pages)
- [**Pagination support for HTTP Request Action**](https://www.tines.com/whats-new/pagination-support-for-http-request-action)
- [**Workbench list conversations API**](https://www.tines.com/whats-new/workbench-list-conversations-api)
- [**Filtering stories with tunnels in use**](https://www.tines.com/whats-new/filtering-stories-with-tunnels-in-use)
- [**Select all events in the console**](https://www.tines.com/whats-new/select-all-events-in-the-console)
- [**Faster troubleshooting with detailed action run failure notification emails**](https://www.tines.com/whats-new/faster-troubleshooting-with-detailed-action-run-failure-notification-emails)
- [**Case activity monitor**](https://www.tines.com/whats-new/case-activity-monitor)

### July 22nd, 2025 - 32.4.0

```
build_sha 32402602_v32_4_0
tines/tines-app: sha256:6ec156add84f7db32dcfacb0cbb19976c77d56eb0b6ab60d7a3e6436c122fcaf
tines/tines-command-runner: sha256:d9db0c849cf63f98f34279359b1d765d081aaa340b85ceff05867661db34f204
tines/tines-nginx: sha256:6b14f60b49fe3f0bca498351e0d33dada1729215dbc1d5c91082183c70d0a7aa
```

- [Custom options when failure occurs action runs](https://www.tines.com/whats-new/custom-options-when-failure-occurs-action-runs)

### July 14th, 2025 - 32.3.0

```
build_sha: 816514db_v32_3_0
tines/tines-app: sha256:85aaa84ebf02440c0f11caebbf6e1c8107ee7d4cf0d08aaaba5945e8fe3629e1
tines/tines-command-runner: sha256:37cd973b6a8b84c82b1a10edc933cc94ebf10f307672e22f0c7d714855acdfed
tines/tines-nginx: sha256:72d7c55c37dda027f132aa803bb3d3b2f2d222cdfb1d67aa6fa5fad392a25e5f
```

- [API to get file metadata on cases](https://www.tines.com/whats-new/api-to-get-file-metadata-on-cases)
- [Code diff view for run script action](https://www.tines.com/whats-new/code-diff-view-for-run-script-action)
- [Story level required change request approvals](https://www.tines.com/whats-new/story-level-required-change-request-approvals)

### July 7th, 2025 - 32.2.0

```
build_sha: 323ab8b5_v32_2_0:
tines/tines-app: sha256:923cead92e69c4f50b86d81563e7d31b4e0e787114d7243994f32deee761e033
tines/tines-command-runner: sha256:339821db6495545f469796f3396951c8adbc2d9a8264f366f66b2a3c19b97504
tines/tines-nginx: sha256:3efef7fb20091dbc27cb4da4b92daee98ca145595f76f12625e6460520b37873
```

- [Workbench API](https://www.tines.com/whats-new/workbench-api)
- [Self-hosted: Update tenant name and domain via environment variables](https://www.tines.com/whats-new/self-hosted-tenants-can-now-update-tenant-name-and-domain-via-environment-variabl)
- [It's now easier to view what's changed in story versions](https://www.tines.com/whats-new/story-diff-modal-updates-in-story-versions)
- [Advanced page element default values](https://www.tines.com/whats-new/reference-meta-page-elements-in-default-values-for-text-fields-in-pages)
- [View active change requests](https://www.tines.com/whats-new/view-active-change-requests)
- [Additional Case note colors](https://www.tines.com/whats-new/additional-case-note-colors)



### July 1st, 2025 - 32.1.0

```
build_sha: 39bbdd29_v32_1_0:
tines/tines-app: sha256:a9896822b5e8bc69d813b77bf6e200c2f00aa27b30e28752737edd9881c5642a
tines/tines-command-runner: sha256:2806a6607d1b21b8ed823a390922c26736b4cff084d6e991c5474e273c9c9a86
tines/tines-nginx: sha256:762e93e931be6ab8399be3dda84cb263fa3477fd2d5972433881f4e51f8f8bfa
```

- [The AI Agent Action](https://www.tines.com/whats-new/introducing-ai-agents)
  
  - Note: We recommend running more sidekiq containers for higher usage of action runs, including AI agents. To monitor the performance of your containers, see our guidance on [monitoring](https://www.tines.com/docs/self-hosting/general-setup-recommendations/standard-recommendations/#monitoring).
- [Cases rich text improvements](https://www.tines.com/whats-new/cases-rich-text-improvements)
- [Re-run saved story runs with mock payloads](https://www.tines.com/whats-new/re-run-saved-story-runs-with-mock-payloads)
- [Bypassing approvals in change control now requires a reason message](https://www.tines.com/whats-new/bypassing-approvals-in-change-control-now-require-a-reason-message)
- [Story monitoring recipients API](https://www.tines.com/whats-new/story-monitoring-recipients-api)
- [Story owners API](https://www.tines.com/whats-new/story-owners-api)
- [Governance setting for pages access control](https://www.tines.com/whats-new/governance-setting-for-pages-access-control)
- [Advanced page element condition rules](https://www.tines.com/whats-new/advanced-page-element-condition-rules)
- [Networking options for run script now available in all cloud tenants](https://www.tines.com/whats-new/run-script-networking-options-available-for-all-cloud-tenants)
- [Run script actions now support "no networking" mode](https://www.tines.com/whats-new/run-script-actions-now-support-no-networking-mode)



### June 27th, 2025 - 32.0.1

```
build_sha: dba75ace_v32_0_1
tines/tines-app: sha256:d3810c8710395e61638001a715669358e3d0d4db62442beee1dde06a18b56a09
tines/tines-command-runner: sha256:db446cc34b9125a4725ec84ba39f466a3d1d16c450a45fc9ae1392c3fb62a150
tines/tines-nginx: sha256:02a3b1790948330b6e715049fc0fc6da81b37f2c138a1480b2cfa4118dd0895c
```

- [Patch fix to make migration more robust](https://www.tines.com/whats-new/login-recovery-codes)



### June 27th, 2025 - 31.5.1

```
build_sha: b28d7e11_v31_5_1
tines/tines-app: sha256:9049e748cc3e681c5520fc655ebd252c0ea412551060bf2fbd817b55580b08e9
tines/tines-command-runner: sha256:a822081f9a061fa6d442ab3482ee1c8f9fa90b845700d07377f07021babf5ee1
tines/tines-nginx: sha256:02a3b1790948330b6e715049fc0fc6da81b37f2c138a1480b2cfa4118dd0895c
```

- [Patch fix to make migration more robust](https://www.tines.com/whats-new/login-recovery-codes)



### June 27th, 2025 - 30.3.6

```
build_sha: a80402c6_v30_3_6
tines/tines-app: sha256:060c400cb166326bef64a03db8269d1c22a507de40433874cb7332e75ab33d72
tines/tines-command-runner: sha256:db446cc34b9125a4725ec84ba39f466a3d1d16c450a45fc9ae1392c3fb62a150
tines/tines-nginx: sha256:d4f5802d17348ebdef4b9cfcbabc2b643dc37e49993f29988bcc093bc700e213
```

- [Patch fix to make migration more robust](https://www.tines.com/whats-new/login-recovery-codes)

### ~~June 24th, 2025 - 32.0.0 ~~[DEPRECATED in favor of 32.0.1]

```
build_sha: d38ee42a_v32_0_0
tines/tines-app: sha256:64fc5f88a30435ae3ec82ce1d2444f201382ba6a3c1ed9d31b8b967a669bbae9
tines/tines-command-runner: sha256:ef05f788e38abacfaeca2bebc5a381a1d58c5a32093acb5d17211c27b772dd53
tines/tines-nginx: sha256:02a3b1790948330b6e715049fc0fc6da81b37f2c138a1480b2cfa4118dd0895c
```

- [~~Duplicate dashboards within teams and case groups~~](https://www.tines.com/whats-new/duplicate-dashboards-within-teams-and-case-groups)
- [~~Run trigger actions with upstream events~~](https://www.tines.com/whats-new/run-trigger-actions-with-upstream-events)
- [~~Login recovery codes~~](https://www.tines.com/whats-new/login-recovery-codes)
- ~~The action performance widget in reporting and action performance API (~~~~`GET /api/v1/reporting/action_performance`~~~~) are sunset.~~



### ~~June 17th, 2025 - 31.5.0 ~~[DEPRECATED in favor of 31.5.1] 

```
build_sha: 2038a62d_v31_5_0
tines/tines-app: sha256:bfae13329f365a87b8c58d1be256fa2e6a968819384d254129ea7d103df83eda
tines/tines-command-runner: sha256:b4fbf0abf5d89a788087339120d1ae3e1c530d5a5691b1b047585ca6bb725e4d
tines/tines-nginx: sha256:fc3af3b2640935da283f4711edd8c57bf610054832ae285815b0cc395c338de9
```

- [Sort case fields within a case](https://www.tines.com/whats-new/sort-case-fields-within-a-case)
- [Open case action pages in new tab](https://www.tines.com/whats-new/case-actions-new-tab)
- [Action egress control for tunnels](https://www.tines.com/whats-new/action-egress-control-for-tunnels)
- [Sort case fields in case settings](https://www.tines.com/whats-new/sorting-case-fields-setting-list)
- [Live and test mode within a record tile](https://www.tines.com/whats-new/record-live-and-test-mode)
- [Direct image upload in cases notes or description](https://www.tines.com/whats-new/direct-image-upload-in-cases)
- [Chart case fields in a dashboard](https://www.tines.com/whats-new/case-field-charts)
- [Cases group block type](https://www.tines.com/whats-new/cases-group-block-type)
- [Improved page change reviews](https://www.tines.com/whats-new/improved-page-change-reviews)

### June 9th, 2025 - 31.4.0

```
build_sha: 6a504e44_v31_4_0
tines/tines-app: sha256:1a42c1929b9c829ba4aa81a9b82675d8591ad9ba7234394fa9bb6c68a33a6fed
tines/tines-command-runner: sha256:d283d1ac7f5cd92ffbcf43b05ab496d49eb09fa3dfbda94f537a8b277c6ad560
tines/tines-nginx: sha256:b63b00cecfcade11f4da7837b5f19dfb3087e002d2807aa6ade57b4deb401373
```

- [Scheduled export of Dashboard snapshots](https://www.tines.com/whats-new/dashboard-snapshots)
- [View more upcoming scheduled action runs](https://www.tines.com/whats-new/view-more-upcoming-scheduled-action-runs)
- [Use custom models with Anthropic](https://www.tines.com/whats-new/use-custom-models-with-anthropic)

### June 2nd, 2025 - 31.3.1

```
build_sha: 685d7644_v31_3_1
tines/tines-app: sha256:48debd7cb0225a8115268d483d7c0001f090ec28ddf008eb2946b762cd4260fb
tines/tines-command-runner: sha256:343fd291959ecf0c37efc450dc6d7981c1f045cb5dbf368208c4f7bb08c5a39b
tines/tines-nginx: sha256:b373c0081bf2dfb071a4eb19534d2ea75a5c50cfd0fe74677d66c9ead6a1b857
```

- Fixes issue with max timeout in tines-command-runner container.
- [New fields in action monitoring webhook payloads](https://www.tines.com/whats-new/new-fields-in-action-monitoring-webhook-payloads)
- [Personalized story suggestions](https://www.tines.com/whats-new/personalised-story-suggestions)
- [Configure AI providers with credentials and formulas](https://www.tines.com/whats-new/configure-ai-providers-with-credentials-and-formulas)
- [Ordered bar charts](https://www.tines.com/whats-new/ordered-bar-charts)
- [Change control redesign](https://www.tines.com/whats-new/change-control-redesign)
- [Improved case activity timeline](https://www.tines.com/whats-new/improved-case-activity-timeline)
- [Page templates](https://www.tines.com/whats-new/page-templates)
- [Improved pages rich text and heading styles](https://www.tines.com/whats-new/improved-pages-rich-text-and-heading-styles)

Note: release 31.3.0 was skipped

### May 30th, 2025 - 31.2.7

```
build_sha: b5c54a4e_v31_2_7
tines/tines-app: sha256:24d7f6f9dd45f37f3979985430cd2cb7bbfa24ac4a3eef3b2f6eb1faf260f40d
tines/tines-command-runner: sha256:5e356c319230dea5ab466d361e4c20843227978b1633787e4c98e75ad17d3584
tines/tines-nginx: sha256:855ec036355615d7b364d7aa6b12ae0282c3d6ff7827d801c9b8c5a979b6a34c
```

- Fixes issue with self-hosted run script not downloading new dependencies when requirements are updated.

### May 27th, 2025 - 31.2.6

```
build_sha: dffa4796_v31_2_6
tines/tines-app: sha256:21e54355f3b53a752087c6a277f35c84588a4073ccd343fc783d3c6a40598d41
tines/tines-command-runner: sha256:d32134a9a23e72a57869f2ce23e6fd91b21ef3833db90ed286dbf77d8d230557
tines/tines-nginx: sha256:855ec036355615d7b364d7aa6b12ae0282c3d6ff7827d801c9b8c5a979b6a34c
```

- Fixes issue syncing integrations during installation and upgrades
- [Release events from throttle and delay event transform actions](https://www.tines.com/whats-new/release-events-from-throttle-and-delay-event-transform-actions)
- [Filter events by re-emitted events](https://www.tines.com/whats-new/filter-events-by-re-emitted-events)
- [Re-emitted events badge](https://www.tines.com/whats-new/re-emitted-events-badge)
- [Custom validation error messages for page inputs](https://www.tines.com/whats-new/custom-validation-error-messages)
- [Navigate between connected pages in the editor](https://www.tines.com/whats-new/navigate-between-connected-pages-in-the-pages-editor)
- [OpenAI tenant level timeout](https://www.tines.com/whats-new/openai-tenant-level-timeout)
- [Events search: custom date and time filters](https://www.tines.com/whats-new/event-search-custom-date-and-time-filters)

Note: releases 31.2.0, 31.2.1, 31.2.2, 31.2.3, 31.2.4 and 31.2.5 were skipped.

### May 20th, 2025 - 31.1.2

```
build_sha: 826be02c_v31_1_2
tines/tines-app: sha256:b75fa00731435de30c2b7583e23ad29b1ede7de88c7b74f760831c9b2462a9c2
tines/tines-command-runner: sha256:c0d3c8bd32acd334576ae6dc62fff70fcd3492e2c0684c72c961f38509a3b955
tines/tines-nginx: sha256:9b0e1082c7ba06cc28f891c28b6db5728ec9d09c967f6b73db929e25b027ff26
```

- Security fixes
- Added an unprivileged nginx image `tines/tines-nginx-unprivileged` which can be deployed to environments where containers cannot be run with root privileges. To use the image, specify `TINES_NGINX_IMAGE=tines/tines-nginx-unprivileged` in your `.env` file. The container runs under user `101` and is based on the [nginx-unprivileged](https://hub.docker.com/r/nginxinc/nginx-unprivileged) image.

### May 15th, 2025 - 31.1.1

```
build_sha: 90b548db_v31_1_1
tines/tines-app: sha256:1807edc60feb98046f71212473f7823105897834523b37130a7cb3cd00dd3244
tines/tines-command-runner: sha256:59f91fb999d0596a5ee3790e41706b69efd63cf4dfbc5876d1847af36a4a377a
tines/tines-nginx: sha256:9b0e1082c7ba06cc28f891c28b6db5728ec9d09c967f6b73db929e25b027ff26
```

- Support running the tines-command-runner with fewer privileges



### May 13th, 2025 - 31.1.0 

```
build-sha: 89c1a61b_v31_1_0
tines/tines-app: sha256:b02a8a3025394241182ba41accaa8a77307f7bc459d6404d4bcac4f2f85434f8
tines/tines-command-runner: sha256:2c33df20ebf341d113d5000b5ba02d29df803f6b72d0abc8ca555702673257ef
tines/tines-nginx: sha256:9b0e1082c7ba06cc28f891c28b6db5728ec9d09c967f6b73db929e25b027ff26
```

- [Case search: Name filter](https://www.tines.com/whats-new/case-search-name-filter)
- [Workbench permissions and role](https://www.tines.com/whats-new/workbench-permissions-and-role)
- [Workbench thinking tool](https://www.tines.com/whats-new/workbench-thinking-tool)
- [Configure AI model with a formula](https://www.tines.com/whats-new/configure-ai-model-with-a-formula)
- [New spacing and width options for page elements](https://www.tines.com/whats-new/new-spacing-and-width-options-for-page-elements)
- [Sync stories between tenants and teams](https://www.tines.com/whats-new/sync-stories-between-tenants-and-teams)
- [Refreshed page elements panel](https://www.tines.com/whats-new/refreshed-page-elements-panel)
- [Audit log filtering improvements](https://www.tines.com/whats-new/audit-log-filtering-improvements)
- [Cases search: Subscribed filter](https://www.tines.com/whats-new/cases-search-subscribed-filter)
- [New external link element for Pages](https://www.tines.com/whats-new/new-external-link-element-for-pages)
- [Higher max timeout for Run Script in cloud](https://www.tines.com/whats-new/higher-max-timeout-for-run-script-in-cloud)
- [Case requirements preview](https://www.tines.com/whats-new/case-requirements-preview)
- [Custom labels for pages boolean elements](https://www.tines.com/whats-new/custom-labels-for-pages-boolean-elements)
- [Comment toggle for case activity](https://www.tines.com/whats-new/comment-toggle-for-case-activity)
- [Page editor inline actions](https://www.tines.com/whats-new/page-editor-inline-actions)
- [Save story runs](https://www.tines.com/whats-new/save-story-runs)
- [April connect flow update](https://www.tines.com/whats-new/april-connect-flow-update)
- [API path prefixes for webhooks](https://www.tines.com/whats-new/api-path-prefixes-for-webhooks)
- [New default webhook path](https://www.tines.com/whats-new/new-default-webhook-path)
- [Looping page elements](https://www.tines.com/whats-new/looping-page-elements)
- [Workbench support for additional file types](https://www.tines.com/whats-new/workbench-support-for-xlsx-xls-doc-and-docx-file-types)
- [Preview a case from the case list view](https://www.tines.com/whats-new/referenced-case-preview)

### May 8th, 2025** - 31.0.2 **

```
build-sha: ab7614df_v31_0_2
tines/tines-app: sha256:621c32693d1a2fa2bdec0f9cc38ae8a5a20d678fae88c050ad9d6d1fee5a2fe7
tines/tines-command-runner: sha256:d60213b86dccdf00d2ce198f8ef22b8e714b39c5326597fd7345866f34419d00
tines/tines-nginx: sha256:9910fd8e34130ac2620294a465a0b40051082d591780ce67926a4276798b5fb7
```

- Security fixes for versions 31.0.0 and 31.0.1

### ~~May 5th, 2025~~~~** - 31.0.1**~~** [DEPRECATED in favor of 31.0.2]**

```
build-sha: c5606352_v31_0_1
tines/tines-app: sha256:42ad7b86f100223da270eb16f63ed3f7dc776a0db67c138720f82a85f64300b6
tines/tines-command-runner: sha256:3fd3eac09f7863e3af562092a7209c5a9d676fb0652d9ccf7bff83106f5ee053
tines/tines-nginx: sha256:08b2eadc2db85c5c01014d8ddced170d7bbcd301c20b396d631e585074ac3711
```

- Patch to fall back to primary DB instance when reader instance experiences lock timeout errors.

### ~~May 5th, 2025~~~~** - 30.3.5 **~~**[DEPRECATED in favor of 30.3.6]**

```
build-sha: 40da1146_v30_3_5
tines/tines-app: sha256:5816a8ccc8465bcd3798e45802d7b7aec8e3e971bfd7d768900bdc7da1c6479b
tines/tines-command-runner: sha256:3fd3eac09f7863e3af562092a7209c5a9d676fb0652d9ccf7bff83106f5ee053
tines/tines-nginx: sha256:3dc44213c5ab7ca8081a7070eab6dce540a51ce192aac4d1356c772df18b7f63
```

- Patch to fall back to primary DB instance when reader instance experiences lock timeout errors.
- Security fixes 

### ~~April 28th, 2025~~~~** - 31.0.0 **~~**[DEPRECATED in favor of 31.0.1]**

```
build-sha: b57e0ae3_v31_0_0
tines/tines-app: sha256:c62273cbf6121374c1b6cb6f4a092b86c4cfde68d92ae028fdce2b6f01c04b03
tines/tines-command-runner: sha256:349152c2b9a192da9188d069217cadef602b7dd65cb0d03d30c1e9413ca239f3
tines/tines-nginx: sha256:cc2dec5df5b6238a29d4ee4d0beea3800517da0353bdd7257d1366e4c7debfe6
```

- [Increased Case block limits](https://www.tines.com/whats-new/increased-case-block-limits)
- [Increase timeout for self-hosted Run Script](https://www.tines.com/whats-new/increase-timeout-for-self-hosted-run-script)
- [CONTAINS\_URL function and planned changes to IS\_URL](https://www.tines.com/whats-new/contains-url-function-and-planned-changes-to-is-url)
- [Webhook support for tenant event limit reached notifications](https://www.tines.com/whats-new/webhook-support-for-tenant-event-limit-reached-notifications)
- [Hidden Case Blocks](https://www.tines.com/whats-new/cases-hidden-blocks)
- [Rich text comments in Cases](https://www.tines.com/whats-new/rich-text-comments-in-cases)
- [Use custom models with the AWS Bedrock AI provider](https://www.tines.com/whats-new/use-custom-models-with-aws-bedrock-ai-provider)
- [Automatically close the tab after submitting a pa](https://www.tines.com/whats-new/automatically-close-the-tab-after-submitting-a-page)
- [Disabling Tunnels](https://www.tines.com/whats-new/disabling-tunnels)
- [Collapse objects within case code snippet](https://www.tines.com/whats-new/collapse-objects-within-cases-code-snippet)
- [Reordering of blocks within the case table of contents](https://www.tines.com/whats-new/drag-and-drop-reordering-for-cases-table-of-contents)

### ~~April 23th, 2025~~~~** - 30.3.4 **~~**[DEPRECATED in favor of 30.3.5]**

```
build-sha: 12d4b427_v30_3_4
tines/tines-app: sha256:040c773aa747e5dbf584b478ee357acc16e65a6ef23ceb43d56871cbd9ae0129
tines/tines-command-runner: sha256:f0ced6b6b5f081c09137799493558d0487fa32efd1969013a755cda1d5b21f61
tines/tines-nginx: sha256:a37527b3623254e7b93e0869085c898b02da2f3fd049e017e965349339014ed9
```

- Introduces support for the `DATABASE_SCHEMA` and `DATBASE_SCHEMA_SEARCH_PATH` environment variables to run Tines on a Postgres schema other than `public`.

### April 22, 2025 - 30.3.3

```
build-sha: c6209bd1_v30_3_3
tines/tines-app: sha256:352850c5a7b123945ede171421a3f0142191f66d241283621ab4b5cf85b53134
tines/tines-command-runner: sha256:3605e76583a0b4f4b76eb6e6dd4456988f4dbdc8a915d610b1675b0b82d9241c
tines/tines-command-runner: sha256:f0ced6b6b5f081c09137799493558d0487fa32efd1969013a755cda1d5b21f61
tines/tines-nginx: sha256:a37527b3623254e7b93e0869085c898b02da2f3fd049e017e965349339014ed9
```

- Re-release of 30.3.2

### April 15th, 2025 - 30.3.2

```
build-sha: c6209bd1_v30_3_2
tines/tines-app: sha256:b174457353807d267220d1d123f5626de20e02ede03e6e7e87e64fcb985322e6
tines/tines-command-runner: sha256:4757617d7eec3d2899fb4d2d9cfcd7b5ed18c63c204c09c0691153492a603ba0
tines/tines-nginx: sha256:6ae5322b45b65e4923cadfbdd62fcd7c0b8188e3b1c47e995d983211d8b631ec
```

- Patch fix for `USE_WORKBENCH` permission causing a crash

### ~~April 14th, 2025 - 30.3.1 ~~**[DEPRECATED in favor of 30.3.2]**

```
build-sha: e877ac3b_v30_3_1
tines/tines-app: sha256:c59e8f8e6822b20657bd10e797a8b1dd7832d0be27ac094557057d1f039431ca
tines/tines-command-runner: sha256:4757617d7eec3d2899fb4d2d9cfcd7b5ed18c63c204c09c0691153492a603ba0
tines/tines-nginx: sha256:6ae5322b45b65e4923cadfbdd62fcd7c0b8188e3b1c47e995d983211d8b631ec
```

- [Case button query parameter](https://www.tines.com/whats-new/case-button-query-parameter)
- [Additional case blocks](https://www.tines.com/whats-new/additional-case-blocks-2)
- [Improvements to cases, records, dashboards experience](https://www.tines.com/whats-new/improved-cases-experience)
- [Cases table of contents 2.0](https://www.tines.com/whats-new/cases-table-of-contents-2-0)
- [Cases file annotations](https://www.tines.com/whats-new/cases-file-annotations)
- [Mention files within a case](https://www.tines.com/whats-new/mentioning-files-in-cases)
- [Configure an API webhook using the stories API](https://www.tines.com/whats-new/configure-an-api-webhook-using-the-stories-api)
- [Reorder pages in a collection directly](https://www.tines.com/whats-new/reorder-pages-in-a-collection-directly)
- [Stop in-progress text generation in Workbench](https://www.tines.com/whats-new/stop-in-progress-text-generation-in-workbench)
- [Private templates in Workbench](https://www.tines.com/whats-new/private-templates-in-workbench)
- Add files to a[https://www.tines.com/whats-new/run-webhooks-for-all-drafts-simultaneously](https://www.tines.com/whats-new/run-webhooks-for-all-drafts-simultaneously)n existing case file attachment block via API
- [Case list enhancements](https://www.tines.com/whats-new/cases-list-enhancements)
- [Configurable retries and timeouts for AI](https://www.tines.com/whats-new/configurable-retries-and-timeouts-for-ai)
- [Creating and cloning private templates](https://www.tines.com/whats-new/creating-and-cloning-private-templates)
- [Reorder page option element options](https://www.tines.com/whats-new/reorder-page-option-element-options)
- [Run webhooks for all drafts simultaneously](https://www.tines.com/whats-new/run-webhooks-for-all-drafts-simultaneously)
- [Search story list API](https://www.tines.com/whats-new/search-story-list-api)

### March 31st, 2025 - 30.2.0

```
build-sha: e5edab8f_v30_2_0
tines/tines-app: sha256:64fe2727c8fc7ffb5bf426b0389f6fca8f6543fbd5d598a0b8062e24dbc53aef
tines/tines-command-runner: sha256:fa5f6db0e68e3b9db7e0c183df1cabce63ec49b2acdc8b390309da81c1cf5ec8
tines/tines-nginx: sha256:afa005d0040826379fcf3704bbdad3226188fd45df3b7d3f5f968a0eced00c03
```

- [Disable a story using stories API](https://www.tines.com/whats-new/disable-a-story-using-stories-api)
- [Workbench file attachments](https://www.tines.com/whats-new/workbench-file-attachments)
- [Inline case actions](https://www.tines.com/whats-new/inline-case-actions)
- [Story owners automatically assigned on story creation](https://www.tines.com/whats-new/story-owners-automatically-assigned-on-story-creation)
- [Support for formula evaluation in webhook rules](https://www.tines.com/whats-new/support-for-formula-evaluation-in-webhook-rules)
- [Check for null values in trigger and webhook action rules](https://www.tines.com/whats-new/check-for-null-values-in-trigger-and-webhook-action-rules)
- [Export drafts through the API](https://www.tines.com/whats-new/export-drafts-through-the-api)
- [Action type filtering in list API](https://www.tines.com/whats-new/action-type-filtering-in-list-api)
- [Action monitoring in stories API](https://www.tines.com/whats-new/action-monitoring-in-stories-api)
- [Cases rich text editor](https://www.tines.com/whats-new/cases-rich-text-editor)
- [View all pages in a team](https://www.tines.com/whats-new/view-all-pages-in-a-team)
- [Records action: get mode](https://www.tines.com/whats-new/records-action-get-mode)
- [Cases tag manager](https://www.tines.com/whats-new/cases-tag-manager)
- [Adding boolean values to credential metadata](https://www.tines.com/whats-new/adding-boolean-values-to-credential-metadata)
- [Run downstream actions with upstream event selection](https://www.tines.com/whats-new/easier-to-run-downstream-actions-with-upstream-events-in-the-ui)
- [Preview pages on different screen sizes](https://www.tines.com/whats-new/preview-pages-on-different-screen-sizes)
- [Python execution for Workbench](https://www.tines.com/whats-new/python-execution-for-workbench)
- [POWER function](https://www.tines.com/whats-new/power-function)
- [Edit and view a collection in separate pages](https://www.tines.com/whats-new/edit-and-view-a-collection-in-separate-pages)
- [Custom layouts for pages](https://www.tines.com/whats-new/custom-layouts-for-pages)
- [Item count indicator when browsing JSON data in event console](https://www.tines.com/whats-new/item-count-indicator-when-browsing-json-data-in-event-console)
- [Improved pages action colors](https://www.tines.com/whats-new/improved-pages-action-colors)
- [Detachable merge conflicts modal](https://www.tines.com/whats-new/detachable-merge-conflicts-modal)
- [Favorite your Workbench conversations](https://www.tines.com/whats-new/favorite-workbench-conversations)
- [RSA_SIGN function](https://www.tines.com/whats-new/rsa-sign-function)
- [Collapsible left panel for page editing](https://www.tines.com/whats-new/collapse-pages-left-panel)
- [Collapsible case blocks](https://www.tines.com/whats-new/collapsible-case-blocks)
- [Filter events by match, no-match and failure paths](https://www.tines.com/whats-new/filter-events-by-match-no-match-and-failure-paths)

### March 21st, 2025** - 30.1.1**

```
build-sha: e82488c2_v30_1_1
tines/tines-app: sha256:26c28ef86850e69181cd896d9bee8b94d14e93ea1e123023949b9e05fca4d3f1
tines/tines-command-runner: sha256:e72f9c9d075bf52e13c6bb21b6e6c22f04b281a97a33ad7a92d5e66921b35018
tines/tines-nginx: sha256:8f397416a85e361e31c631d0696d29da2cbc065f9e7c5812c52bf9cd4be1f9d2
```

- Skipped release. No changes since 30.1.0

### March 17th, 2025** - 30.1.0**

```
build-sha: e82488c2_v30_1_0:
tines/tines-app: sha256:1cb99303c460486fb8375552e53d7bfca5c9b9fb302ed167fbc075d664c88be6
tines/tines-command-runner: sha256:6b3d8a23b0403ceb0aff8b69246968389d9858e1b1cf90b82ebd57c62eb5de06
tines/tines-nginx: sha256:8f397416a85e361e31c631d0696d29da2cbc065f9e7c5812c52bf9cd4be1f9d2
```

- [Copy items as path or full text from Event console](https://www.tines.com/whats-new/copy-items-as-path-or-full-text-from-event-console)
- [Linked case preview](https://www.tines.com/whats-new/case-preview-tooltips)
- [Reference page elements](https://www.tines.com/whats-new/reference-page-elements-using-formulas)
- [Resolve merge conflicts in draft stories](https://www.tines.com/whats-new/resolve-merge-conflicts-in-draft-stories)
- [Pill result tooltips](https://www.tines.com/whats-new/pill-result-tooltips)
- [Easily drag and drop files to import Stories](https://www.tines.com/whats-new/easily-drag-and-drop-files-to-import-stories)
- [CONST_EQUALS function](https://www.tines.com/whats-new/const-equals-function)
- [Add users count to user and teams lists ](https://www.tines.com/whats-new/add-users-count-to-user-and-teams-lists)
- [Add names to page element containers](https://www.tines.com/whats-new/add-names-to-page-element-containers)
- [Improved filtering in the story runs API](https://www.tines.com/whats-new/improved-filtering-in-the-story-runs-api)
- [Median function](https://www.tines.com/whats-new/median-function)

### March 13th, 2025** - 30.0.2**

```
build-sha: 8340a4f7_v30_0_2
tines/tines-app: sha256:630b37ef9d746bc64a77e153878825f7936a34200a6d710556725d7e6fafb4f6
tines/tines-command-runner: sha256:a67b2e297ab0b3a0fe4f5d264051f60ecd8ea623fed4c4e2531814b33cb1d21e
tines/tines-nginx: sha256:0c52ff37da94dd361a86b726968aa9a664a437a2f603173cedcffa1639b43129
```

- Security fixes
- Reenable story library syncing

### March 11th, 2025** - 30.0.1**

```
build-sha: 9b31bc72_v30_0_1
tines/tines-app: sha256:15c5581886dbbf8bb5181f885244acead92e255ea8b317439ae65de099fee1c0
tines/tines-command-runner: sha256:1d9ab111a6341787bba1ed532a3544076df217a64ffae35d2e99574a8b7cd68f
tines/tines-nginx: sha256:0c52ff37da94dd361a86b726968aa9a664a437a2f603173cedcffa1639b431
```

- Security improvements

### March 10th, 2025** - 30.0.0**

```
build-sha: 3cd57db9_v30_0_0
tines/tines-app: sha256:fc2f5a5df201e1a90635b7a2b6a0c802d20532f312aa999a17d13a96daf494fe
tines/tines-command-runner: sha256:c7e7f91461a0768aa5b7d0025be6bf49524325f8475c9193ba06b0e45d667804
tines/tines-nginx: sha256:0c52ff37da94dd361a86b726968aa9a664a437a2f603173cedcffa1639b43129
```

- [Change control draft details in `META` tag](https://www.tines.com/whats-new/change-control-draft-details-in-meta-tag)
- [`GROUP_BY` now supports `LAMBDA`](https://www.tines.com/whats-new/group-by-now-supports-lambda)
- [OpenAI reasoning models](https://www.tines.com/whats-new/openai-reasoning-models)
- [Test credential metadata](https://www.tines.com/whats-new/test-credential-metadata)
- [Configurable columns on the Stories table](https://www.tines.com/whats-new/configurable-columns-on-the-stories-table)
- [Cases cycle buttons](https://www.tines.com/whats-new/cases-cycle-buttons)
- [Delete drafts API](https://www.tines.com/whats-new/delete-drafts-api)
- [Team scoped tags](https://www.tines.com/whats-new/team-scoped-tags)
- [Query params option for HTTP Request Action](https://www.tines.com/whats-new/query-params-option-for-http-request-action)
- [Assign role on user invite](https://www.tines.com/whats-new/assign-role-when-making-a-user-a-member-of-a-team)
- [Simplified collectin setup](https://www.tines.com/whats-new/simplified-collection-setup)
- [Detachable change request modal](https://www.tines.com/whats-new/detachable-change-request-modal)
- [Share workbench conversations](https://www.tines.com/whats-new/share-workbench-conversations)
- [Edit workbench presets](https://www.tines.com/whats-new/edit-workbench-presets)
- [AI action output structure](https://www.tines.com/whats-new/ai-action-output-structure)
- [Group page elements in containers](https://www.tines.com/whats-new/group-page-elements-in-containers)
- [Cases file attachment block](https://www.tines.com/whats-new/case-file-attachment-block)
- [Support for gRPC requests](https://www.tines.com/whats-new/grpc-support)
- [Cases block API endpoints](https://www.tines.com/whats-new/cases-block-api-endpoints)
- [Writing formulas with Workbench](https://www.tines.com/whats-new/writing-formulas-with-workbench)
- [View queued events in memory for Event Transformation action delay mode](https://www.tines.com/whats-new/view-queued-events-in-memory-for-event-transformation-action-delay-mode)
- [Start a new Workbench conversation from the command palette](https://www.tines.com/whats-new/start-a-new-workbench-conversation-from-the-command-palette)
- [Update to URL_ENCODE function](https://www.tines.com/whats-new/update-to-url-encode-function)
- [Run Script added to Tools menu](https://www.tines.com/whats-new/run-script-added-to-tools-menu)
- [Workbench kickoff location](https://www.tines.com/whats-new/workbench-kickoff-location)
- [Events search: scroll through results](https://www.tines.com/whats-new/events-search-scroll-through-results)
- [Refer to URL query parameters in pages](https://www.tines.com/whats-new/refer-to-url-query-parameters-in-pages)
- [Send to story action now features the story icon](https://www.tines.com/whats-new/send-to-story-action-now-features-the-story-icon)
- [`IN_CIDR` now takes an array of addresses](https://www.tines.com/whats-new/in-cidr-now-takes-an-array-of-addresses)
- [Save list or board display preference with case view](https://www.tines.com/whats-new/cases-saved-views-display-preference)
- [Capture raw body option for Webhooks](https://www.tines.com/whats-new/capture-raw-body-option-for-webhooks)
- [Improved user filtering](https://www.tines.com/whats-new/improved-user-filtering)
- [Improve filtering in credentials API](https://www.tines.com/whats-new/improve-filtering-in-credentials-api)
- [Improve filtering in resources API](https://www.tines.com/whats-new/improve-filtering-in-resources-api)
- [API endpoint to monitor web server health](https://www.tines.com/whats-new/api-endpoint-to-monitor-web-server-health)
- [`TO_SNAKE_CASE` can now convert from camel case](https://www.tines.com/whats-new/to-snake-case-can-now-convert-from-camel-case)
- [Re-enable action by clicking the disabled status badge](https://www.tines.com/whats-new/re-enable-action-by-hitting-disabled-status-badge)
- [New connect flows](https://www.tines.com/whats-new/new-connect-flows)
- [Slider element for pages](https://www.tines.com/whats-new/slider-element-for-pages)
- [✨ New Claude 3.7 Sonnet model powers Workbench](https://www.tines.com/whats-new/new-claude-3-7-sonnet-model-powers-workbench)
- [Filter Notifications](https://www.tines.com/whats-new/filter-notifications)
- [New event transform mode indicators](https://www.tines.com/whats-new/new-event-transform-mode-indicators)
- [Records action: list mode](https://www.tines.com/whats-new/records-list-mode)
- [`CAMELIZE` function](https://www.tines.com/whats-new/camelize-function)
- [Setup a page from an existing page](https://www.tines.com/whats-new/setup-a-page-from-an-existing-page)
- [Improved story creation](https://www.tines.com/whats-new/improved-story-creation)
- [Rewinding Workbench conversations](https://www.tines.com/whats-new/rewinding-workbench-conversations)
- [Storyboard header redesign](https://www.tines.com/whats-new/storyboard-header-redesign)
- [Amazon Nova models available in the AI action](https://www.tines.com/whats-new/amazon-nova-models-available-in-the-ai-action)
- [AI action credit usage reporting](https://www.tines.com/whats-new/ai-action-credit-usage-reporting)
- [`IS_YAML` strict mode](https://www.tines.com/whats-new/is-yaml-strict-mode)

### Older releases (<30.0.0)

> **TIP:** Get notified of new releases by following the [Self-hosting release notes RSS feed](https://www.tines.com/rss/tines-self-hosting-release-notes.xml).

### July 24th, 2025 - 26.3.5

```
build_sha bbb9f9d7_v26_3_5:
tines/tines-app: sha256:476d4c2be4b2deb11d3f69c79d95914a6cebdc7dc8ae8c06913895e9db79e6f4
tines/tines-nginx: sha256:c3aff578ae7fedce78767c86db1aecce213729bbb547e635cab192e68be7c0ab
```

- Fixed an issue blocking upgrades from 25.x in some environments 

### ~~July 16th, 2025 - 26.3.4~~ **[DEPRECATED IN FAVOR OF 26.3.5]**

```
build_sha 26010f17_v26_3_4:
tines/tines-app: sha256:0218ff44570b7c909a48f101a882c6dd73ba8003078b1619eadcd4d013382f40
tines/tines-nginx: sha256:171d5dd4e9b9da6535c849dbe6c32c6c3d3f7fb6a1b6d68f77a61c46b5cb3a89
```

- Fixed an issue blocking upgrades from 25.x in some environments 

### April 23rd, 2025** - 29.1.6 **

```
build-sha: 9341faa0_v29_1_6
tines/tines-app: sha256:be7cd25b9c700555731aca034d8c12bd7e47d183abb4ca4962d36dcda4a07d77
tines/tines-command-runner: sha256:6cd9b27d326ba842bd345f20c3143e58f700759888341e33fa0e972cf71ca1a7
tines/tines-nginx: sha256:ae188ff8e07215964a3ad0453a834f3dffdc3691c35b9c2a451c8303ab1a666b
```

- Introduces support for the `DATABASE_SCHEMA` and `DATBASE_SCHEMA_SEARCH_PATH` environment variables to run Tines on a Postgres schema other than `public`.
- Improves tenant setup process with missing environment variables.

### ~~April 23rd, 2025~~~~** - 29.1.5**~~** [DEPRECATED in favor of 29.1.6]**

```
build-sha: 6d1f679e_v29_1_5
tines/tines-app: sha256:29b15d072938117aaebfa903e3d68a67015f735583c9910cbe4bb78cce5b3ea6
tines/tines-command-runner: sha256:f8f3e3020f2d5bdfd767803aaaed5fbebb5d2ece25537a51d4e3c56a19309cc6
tines/tines-nginx: sha256:fb0b19d5a2615548d5e019ff3b95fdc74701cdbf12040abbb50b4f6f494b3e07
```

### March 13th, 2025** - 29.1.4**

```
build-sha: 410b9301_v29_1_4
tines/tines-app: sha256:0c3ca098a35bdbdf869e4caeb8140095bc2b97086c6a4b9bd94cc2f6af675d49
tines/tines-command-runner: sha256:fe4989c31c26760305f413653138de706a5280dc514aa05b05c1bc88ce5d4b2f
tines/tines-nginx: sha256:375f21a3c29d0802cf5e42818b3ab4ccfe698b8024699037c3c069160e938b34
```

- Security fixes

### March 11th, 2025** - 29.1.3**

```
build-sha: 4689eba6_v29_1_3
tines/tines-app: sha256:18f3620f6d7a338641db0c588d53224a3dfaccf92e11b385eda7a0fca9c59a71
tines/tines-command-runner: sha256:356b309a40b5a614084b8e089ec6029061afae94879564eb885be6d10b63286e
tines/tines-nginx: sha256:08f951a49f6c5fbbd595c86dde1bad51c73081f531992a1f2ff027fea2fe51f7
```

- Patch fix for a bug in Story Library sync that can exhaust memory on Tines Web containers

### March 11th, 2025** - 28.3.3 **

```
build-sha: 91520353_v28_3_3
tines/tines-app: sha256:bafdb2becb21cacfcb073f19fea09b4f106202c9b2683e73eee17fd3f02e3912
tines/tines-command-runner: sha256:6573cc604370bfc074fc4436b4f84e80b718361ff68464582daa18735a60b7bd
tines/tines-nginx: sha256:57983ec9d7955a370468a2db6a6b87854abf5635933ddc26455df07dafb6694c
```

- Patch fix for a bug in Story Library sync that can exhaust memory on Tines Web containers

### February 26th, 2025** - 29.1.2**

```
build-sha: eebddec4_v29_1_2
tines/tines-app: sha256:dc30510e1bb6b0d87fd08dd383de110c97462ee8690e91badeed0477dfa559d9
tines/tines-command-runner: sha256:f8fe2787d3ab62cbec0f5ae817ffd337fa50f1fb0459ca62885622b3bbd8698c
tines/tines-nginx: sha256:21f4c7b7379cab9fb7d6470b2d3a914fcbe18e8f682acee92a8de498e86970ad
```

> **NOTE:** Upgrades Tines PostgreSQL images from 14.15 -> 14.17 in order to guard against an SQL injection vulnerability.

### February 18th, 2025** - 29.1.1 **

```
build-sha: 214758ca_v29_1_1
tines/tines-app: sha256:f4f9174ffcf6283be874391401e3140e02b77048719d50546f4ca1ec1681f8ca
tines/tines-command-runner: sha256:56bee8793d2bcec98d82e908fb3bb9c0b6b182875b11f21394017c39507a1673
tines/tines-nginx: sha256:b8f71c1a2d801a2c488e122bc097e0b8cdc8abab10fa07f8c93a4007321c8ac0
```

- Patch for tines-command-runner to allow uv to create managed python versions 
- Patch of StoryChangeControl to enable multiple drafts
- Correction to schema validation for major versioning

### ~~February 18th, 2025 - 29.1.0~~** [DEPRECATED IN FAVOR OF 29.1.1]**

### ~~February 17th, 2025 - 29.0.0~~  **[DEPRECATED IN FAVOR OF 29.1.1]**

```
build-sha: 48f24b82_v29_0_0
tines/tines-app: sha256:4846f2f5dce8b297e44c8d781c32404b1223fae93c96ba46691bfc5c8cfbe65c
tines/tines-command-runner: sha256:413d7599a20b22e0759584523298525c308db978eb95e513a98157e6f17c0468
tines/tines-nginx: sha256:3ff8fa34d85e261d71beace12bb8d19c2556db9a4894e872242574bee6208aac
```

- [~~Additional page images sizing options~~](https://www.tines.com/whats-new/additional-page-images-sizing-options)
- [~~Azure OpenAI Support~~](https://www.tines.com/whats-new/azure-openai-support)
- [~~Only show changed attributes in view changes modal~~](https://www.tines.com/whats-new/only-show-changed-attributes-in-view-changes-modal)
- [~~Duplicate drafts~~](https://www.tines.com/whats-new/duplicate-drafts)
- [~~Match now supports modifiers~~](https://www.tines.com/whats-new/match-now-supports-modifiers)
- [~~Story monitoring status and recipients in stories API~~](https://www.tines.com/whats-new/story-monitoring-status-and-recipients-in-stories-api)

### February 12th, 2025 - 28.3.2

```
build-sha: 2eae9b00_v28_3_2
tines/tines-app: sha256:8cbaec12347414b9f19fe9e14abfb8ddac3150f42bc1a8556a81c36b12d33d41
tines/tines-command-runner: sha256:df9e2ac72cf4298ca7fb9e1d61184b82965e57b20e161802cf233728a1e66b6d
tines/tines-nginx: sha256:993f431c2fffe90261ccf26ee3fdd20e61baa9164b17b81394aa41cdd8322208Bug fix to prevent Segfault errors from ProcessDelayedEventsJob
```

- Bug fix to enable FIPs support for the AWS Bedrock AI provider
- Disabled workbench message warning
- Updated net-imap

### February 11th, 2025 - 28.3.1

```
build-sha: fc7a61ad_v28_3_1
tines/tines-app: sha256:dac7aff2eaff4c366f595e9ddad1b97df3450c71a00601f1a1d6a335b601fe4d
tines/tines-command-runner: sha256:927b90c9c65d19be9660eb80db7f49539be80f49e6d60bbcb0be9684b8785a35
tines/tines-nginx: sha256:993f431c2fffe90261ccf26ee3fdd20e61baa9164b17b81394aa41cdd8322208
```

- Bug fix to prevent Segfault errors from ProcessDelayedEventsJob

### February 10th, 2025 - 28.3.0

```
build-sha: 56a52c82_v28_3_0
tines/tines-app: sha256:fc81ab2c8d5d9d2dbd16fc4d4ca67888de765266d3a0f7d04ffbe763ae7569db
tines/tines-command-runner: sha256:3d3c874e8594e5f2f8691e7153e7c31b688b1e0cbe1e259c83aaf8dd7f5cc9be
tines/tines-nginx: sha256:993f431c2fffe90261ccf26ee3fdd20e61baa9164b17b81394aa41cdd8322208
```

- [Bring your own AI models to Tines](https://www.tines.com/whats-new/bring-your-own-ai-models-to-tines)
- [Workbench in Slack](https://www.tines.com/whats-new/workbench-in-slack)
- [Formula mode in HTTP action headers](https://www.tines.com/whats-new/formula-mode-in-http-action-headers)
- [GROUP_BY function](https://www.tines.com/whats-new/group-by-function)
- [Get a page viewer’s email address](https://www.tines.com/whats-new/get-a-page-viewer-s-email-address)
- [Rate limit configuration for Webhooks](https://www.tines.com/whats-new/rate-limit-configuration-for-webhooks)
- [Improvements to users settings UI](https://www.tines.com/whats-new/improvements-to-users-settings-ui)
- [TITLEIZE function](https://www.tines.com/whats-new/titleize-function)
- [Paste from spreadsheets to Markdown](https://www.tines.com/whats-new/paste-from-spreadsheets-to-markdown)
- [Use formulas in page element conditions](https://www.tines.com/whats-new/use-formulas-in-page-element-conditions)
- [New encryption functions](https://www.tines.com/whats-new/new-encryption-functions)
- [API endpoint for listing fields from a case input](https://www.tines.com/whats-new/api-endpoint-for-listing-fields-of-a-case-input)
- [Refreshed pages editor UI](https://www.tines.com/whats-new/refreshed-pages-editor-ui)
- [Case notes](https://www.tines.com/whats-new/case-notes)
- [Drag and drop items in the JSON builder](https://www.tines.com/whats-new/drag-and-drop-items-in-the-json-builder)
- [New parameter for DATE_DIFF function](https://www.tines.com/whats-new/new-face-on-date-diff-function)
- [In-app notifications of event limits](https://www.tines.com/whats-new/in-app-notifications-of-event-limits)

### January 20th, 2025 - 28.2.0

```
build-sha: a65d2063_v28_2_0
tines/tines-app: sha256:46293f790aed105b3906552b6c14747bde2cbd2048d34dc199eaef3feea5a236
tines/tines-command-runner: sha256:9a5ada16a7bd56ed4c8b7f0f2cea43e0fcb766cd6fe1948ddc20b58b1236b2a1
tines/tines-nginx: sha256:fc807675d0f75e9c22606f3f4fba82b2ee49616bcb2205ce0c3a497346b09d61
```

- [Preview output when building actions](https://www.tines.com/whats-new/preview-output-when-building-actions)
- [Drafts are now personal teams](https://www.tines.com/whats-new/drafts-are-now-personal-teams)
- [Adding SWITCH function](https://www.tines.com/whats-new/adding-switch-function)
- [Custom change control permissions](https://www.tines.com/whats-new/custom-change-control-permissions)

### January 20th, 2025 - 28.1.1

```
build-sha: cdabf44c_v28_1_1
tines/tines-app: sha256:29aa606b27cd955afdbfeb9dacb48cfb2708fbbc4f1328d34c31ae321559c577
tines/tines-nginx: sha256:fc807675d0f75e9c22606f3f4fba82b2ee49616bcb2205ce0c3a497346b09d61
```

- Tag creation fixes
- Dockerfile updated

> **NOTE:** We've updated our supported PostgreSQL version to 14.15 in order to guard an SQL injection vulnerability.

### January 15th, 2025 - **28.1.0**

```
build-sha: be056338_v28_1_0
tines/tines-app: sha256:c6e4ca4d84e69f8fa14efde323e6abd65e2e36a9d2dcd4485343e9610185fb46
tines/tines-command-runner: sha256:b15e6dfb8cbb2604c7f67e6234a13b6e0cb3e8f5524b56f35c4e7d047daf344a
tines/tines-nginx: sha256:9fa8777ec3c2d695096ccc44c650620924f720ca1ea5af2577cb83c271e58181
```

- Upgrades Tines PostgreSQL images from 14.5 -> 14.15
- [Send inline email attachments](https://www.tines.com/whats-new/send-inline-email-attachments)
- [Change attribution](https://www.tines.com/whats-new/change-attribution)
- [Locate users drafts](https://www.tines.com/whats-new/locate-users-drafts)
- [Parallel LOOP option for Send to Story and Groups](https://www.tines.com/whats-new/parallel-loop-option-for-send-to-story-and-groups)
- [Workbench: Reply Suggestions](https://www.tines.com/whats-new/workbench-user-reply-suggestions)
- [Cases search ui refresh](https://www.tines.com/whats-new/cases-search-ui-refresh)
- [Send to story inputs modal](https://www.tines.com/whats-new/send-to-story-inputs-modal)
- [Hide positional changes in view changes modal](https://www.tines.com/whats-new/hide-positional-changes-in-view-changes-modal)

### January 10th, 2025 - **28.0.4**

```
build-sha: fa3562c1_v28_0_4
tines/tines-app: sha256:8b738784fe8c15f29af11faba48ffb028867b09c1d741a488e8cd7ad1e80fc25
tines/tines-command-runner: sha256:42f4a2b7673678535c2276b88a2992e67159ac43e35be981e4a0cc3e4173c053
tines/tines-nginx: sha256:7aa0f7f2dc28eaf8c0f8f3faf38274333e292bb46d2e9a70455f7504ed6d2389
```

- [Updated credential selection in templates](https://www.tines.com/whats-new/updated-credential-selection-in-templates)
- [Submit pages with table row selections](https://www.tines.com/whats-new/submit-pages-with-table-row-selections)
- [Searchable option fields for pages](https://www.tines.com/whats-new/searchable-option-fields-for-pages)
- [Update to AES_ENCRYPT function](https://www.tines.com/whats-new/update-to-aes-encrypt-function)
- [HTTP parameters in templates](https://www.tines.com/whats-new/http-parameters-in-integrations)
- [View record IDs in records table](https://www.tines.com/whats-new/record-ids-in-record-tables)
- [Reordering Case actions](https://www.tines.com/whats-new/reordering-case-actions)
- [Expiration Support for AES_ENCRYPT](https://www.tines.com/whats-new/expiration-support-for-aes-encrypt)
- [Send to Story LOOP option support for array processing](https://www.tines.com/whats-new/send-to-story-loop-option-support-for-array-processing)
- [Group LOOP option support for array processing](https://www.tines.com/whats-new/group-loop-option-support-for-array-processing)
- [Expire user sessions via the API](https://www.tines.com/whats-new/expire-user-sessions-via-the-api)

### ~~ January 7th, 2025 - ~~~~**28.0.3**~~** [DEPRECATED IN FAVOR OF 28.0.4]**

### ~~January 6th, 2025 - ~~~~**28.0.2**~~** **[DEPRECATED IN FAVOR OF 28.0.3]

### ~~January 6th, 2025 - ~~~~**28.0.1**~~** **[DEPRECATED IN FAVOR OF 28.0.3]

### ~~January 6th, 2025 - ~~~~**28.0.0**~~** **[DEPRECATED IN FAVOR OF 28.0.3]

### December 11th, 2024 - **27.3.0**

```
build-sha: 2e8884d3_v27_3_0
tines/tines-app: sha256:5287fe662a1b5b21500349ffbd2cb2d3f64d636e92442173d23d566203758b66
tines/tines-nginx: sha256:a0f89cc736d3a32cc37c39b2dfc6809615069a94986f5f162554c49d8e78cf8c
```

- Support AI on Self-hosted
- [Cases list mobile view](https://www.tines.com/whats-new/cases-list-mobile-view)

### December 9th, 2024 - **27.2.0**

```
build-sha: 91349c1d_v27_2_0
tines/tines-app: sha256:145f43a6e4c75b1c87017e0930b44898bb924630f9884825cb10c4ca18ab119a
tines/tines-nginx: sha256:a0f89cc736d3a32cc37c39b2dfc6809615069a94986f5f162554c49d8e78cf8c
```

- [Link directly to an action using `STORY_RUN_LINK()`](https://www.tines.com/whats-new/link-directly-to-an-action-using-story-run-link)
- [Additional options when creating conditional page elements](https://www.tines.com/whats-new/additional-options-when-creating-conditional-page-elements)
- [AI action JSON mode](https://www.tines.com/whats-new/ai-action-json-mode)
- [Linked case templates](https://www.tines.com/whats-new/linked-case-templates)
- [Search events using time range filters](https://www.tines.com/whats-new/search-events-using-time-range-filters)
- [Use your own OpenAI and Anthropic API keys](https://www.tines.com/whats-new/use-your-own-openai-and-anthropic-api-keys)

### December 2nd, 2024 - 27.1.3

```
build-sha: a3f10fc8_v27_1_3
tines/tines-app: sha256:9136dd7e929c697aeaed05a50dfa6ec88fa09e06bcc48149d7f933df3b5e15c3
tines/tines-nginx: sha256:0d43cfa4e232beb42952893efbfdf3f178a7c37cbd88b3d5bd330dc88d3f311f
```

- Updating the latest tag to match the new release on 27.x

### November 27th, 2024 - 27.1.2

```
build-sha: a3f10fc8_v27_1_2
tines/tines-app: sha256:f1cde9bb996ce463bd5ae7ca043308f729c8010aefdbdfd44132323512bc5de3
tines/tines-nginx: sha256:7957e2b8441db207fc0d23f2fa59f0baec1fb8d2d34f2b627d60f39f21972cd4
```

- Tines tunnel security updates for CVE-2024-38428, CVE-2024-10524
- Rails migration index exception handling fix

### ~~November 27th, 2024 - 26.3.3 ~~**[DEPRECATED IN FAVOR OF 26.3.5]**

```
build-sha: ef288da7_v26_3_3
tines/tines-app: sha256:dba5736a5e18a2899024600b18677011e9c5ee036e9aef9afccc4009f568ad58
tines/tines-nginx: sha256:bda37c9861ba2b1e95aa49942646023063cbafa3549cd7b095aaea3480354c75
```

- Rails migration index exception handling fix

### November 27th, 2024 - 25.2.7

```
build-sha: f8527886_v25_2_7
tines/tines-app: sha256:7ef7e79b8465517809b4a395cc4e4eda70fdf85c3dcb321f1bbdbc97dc58ba50
tines/tines-nginx: sha256:6c8a9de8a9b6712c1a87d51e6e056e1071d31740d23241e0fe49a5db54bfe284
```

> **NOTE:**
> Please space out your last upgrade from 25.2.7 to 26.3.3 by at least one week. We are shipping some background maintenance to improve how action runs and action run-specific data modeling are performed. As part of this maintenance, we have shipped some tasks that backfill the data behind the scenes without any impact on action runs.
> 
> To ensure a smooth transition, we request that you allow at least one week between your last upgrade on 25.2.7 and the upgrade to 26.3.3.

- Rails migration index exception handling fix

### November 26th, 2024 - 27.1.1

```
build-sha: 1e07d2a5_v27_1_1
tines/tines-app: sha256:580b00f362ef2dabb0c0445c4a867e8f8883dd53d0eef83ff8eaa5e2ef5d1d65
tines/tines-nginx: sha256:0b42313cae776c2ffd12de60ba40875b3232d0a5a458293e8aca21f5ad6c49f0
```

- Introduce `RACK_KEY_SPACE_LIMIT` optional environment variable
- Fix CORS option check on webhooks

### November 25th, 2024 - 27.1.0

```
build-sha: 9549aaf4_v27_1_0
tines/tines-app: sha256:b5e0135f5e68977ded59fcf854a2e2c0630df31840f0aa45aac33a0bf24bc179
tines/tines-nginx: sha256:0b42313cae776c2ffd12de60ba40875b3232d0a5a458293e8aca21f5ad6c49f0
```

- [Run script support for self-hosted ](https://www.tines.com/whats-new/run-script-support-for-self-hosted)
- [Show, hide, and disable page elements conditionally](https://www.tines.com/whats-new/show-hide-and-disable-page-elements-conditionally)
- [Improved linked Case search](https://www.tines.com/whats-new/improved-linked-case-search)
- [100 connect flows](https://www.tines.com/whats-new/100-connect-flows)
- [Settings Center](https://www.tines.com/whats-new/settings-center)
- [Cases mobile view](https://www.tines.com/whats-new/cases-mobile-view)
- [Stacked bar charts for Records & Dashboards](https://www.tines.com/whats-new/stacked-bar-charts-for-records-dashboards)
- [Tenant permissions](https://www.tines.com/whats-new/tenant-permissions)
- [Directly export case list](https://www.tines.com/whats-new/directly-export-case-list)

### November 18th, 2024 - 27.0.2

```
build-sha: 97926aae_v27_0_2
tines/tines-app: sha256:05be621a4e2d1a1fe4a695db568cafe9989b6339ccf93848a9e2f0cb39aca456
tines/tines-nginx: sha256:444207eb4f063d7dae0a3f2aee4327f23c971e077d075e228150982833471603
```

- Fix for major version upgrade check

### November 14th, 2024 - 27.0.1

```
build-sha: 78a79bf0_v27_0_1
tines/tines-app: sha256:e8cf24963ad9bf2f1811ede03f70956804ea6dc92d36dbac5e2b3c3fc1d26ce9
tines/tines-nginx: sha256:9d98c96f8099e4a529211cf2f66e7fb2d4f17e7019091faf3324c39cb54a2635
```

Note: this image has been updated with different digests, but it is still the same version. These are also valid:

```
tines/tines-app: sha256:8749e627d3be290f31d6ec7479cc5783bee0ed447567a72ac8181ec746f339e5
tines/tines-nginx: sha256:444207eb4f063d7dae0a3f2aee4327f23c971e077d075e228150982833471603
```

- Bug fix when upgrading to new release

### November 11th, 2024 - 27.0.0

```
build-sha: b8282d6d_v27_0_0
tines/tines-app: sha256:4abaf732f37a32930329bab7de9c3e22307e8c358093054188c184888fde4022
tines/tines-nginx: sha256:6f210397f4e502173f50b5ade24f0fb54a514df1a945c19257291135ce5dc2f9
```

- [Workbench presets](https://www.tines.com/whats-new/workbench-presets)
- [AI Action Metadata](https://www.tines.com/whats-new/ai-action-metadata)
- [Easily reference previous event's payload using META](https://www.tines.com/whats-new/easily-reference-previous-event-s-payload-using-meta)
- [Case fields in Cases list table](https://www.tines.com/whats-new/case-fields-in-cases-list-table)
- [Assigned user Case cycling](https://www.tines.com/whats-new/assigned-user-case-cycling)
- [Enable change control on story creation per team](https://www.tines.com/whats-new/enable-change-control-on-story-creation-per-team)
- [Improved Case Records functionality](https://www.tines.com/whats-new/improved-case-records-functionality)
- [Bulk delete records](https://www.tines.com/whats-new/bulk-delete-records)
- [New Claude 3.5 Sonnet model powers Workbench](https://www.tines.com/whats-new/new-claude-3-5-sonnet-model-powers-workbench)
- [Failure path for actions](https://www.tines.com/whats-new/failure-path-for-actions)
- [Action inputs for private templates](https://www.tines.com/whats-new/action-inputs-for-private-templates)
- [New 'emit failure event' option](https://www.tines.com/whats-new/new-emit-failure-event-option)
- [New 'log error if' option](https://www.tines.com/whats-new/new-log-error-if-option)
- Environment variable fixes: properly loading `RAILS_LOG_LEVEL` and `RAILS_LOG_TO_STDOUT`

### November 6th, 2024 - 26.3.2

```
build-sha: d5a684f2_v26_3_2
tines/tines-app: sha256:f9247edafd8763eaef633d057f2d94c58fb4a1cf6985e04b2e620c3fc8768cf6
tines/tines-nginx: sha256:91d8cc54ef3d6220711bddba3cb79c23a2fbe490e30dc81d357beaefbb09053fBug fix: Ensure actions inside group do not run when story is disabled
```

- Bug fix when updating last seen on user sign in

### November 1st, 2024 - 26.3.1

```
build-sha: f9632fff_v26_3_1
tines/tines-app: sha256:d6638ebb8a972ccf856051487ebfc4665728e84d56849163268ce9a302ab8bb6
tines/tines-nginx: sha256:1c6007bbb2538faf562325fa3180cf96054df932cedb6f85e0cd6c458e01309c
```

- Bug fix: Ensure actions inside group do not run when story is disabled

### October 28th, 2024 - 26.3.0

```
build-sha: 18bc26f0_v26_3_0
tines/tines-app: sha256:360076c18ab9e8719feabef44ab8c33e814816043c7b161dfcb12e6914f6ae15
tines/tines-nginx: sha256:1c6007bbb2538faf562325fa3180cf96054df932cedb6f85e0cd6c458e01309c
```

- [Configurable story owners](https://www.tines.com/whats-new/configurable-story-owners)
- [Right click to insert story actions and control the storyboard](https://www.tines.com/whats-new/right-click-to-insert-story-actions-and-control-the-storyboard)
- [Search credential types](https://www.tines.com/whats-new/search-credential-types)
- [View and manage credentials tenant-wide](https://www.tines.com/whats-new/view-and-manage-credentials-tenant-wide)
- [Require name and description for change requests](https://www.tines.com/whats-new/require-name-and-description-for-change-requests)
- [Send Email allowlist](https://www.tines.com/whats-new/send-email-allowlist)
- [Tines Tunnel Deletion](https://www.tines.com/whats-new/tines-tunnel-deletion)
- [View a story run on the diagram](https://www.tines.com/whats-new/view-a-story-run-on-the-diagram)
- [HTTPS Only for HTTP Request Actions](https://www.tines.com/whats-new/https-only-for-http-request-actions)
- [Case Deletion](https://www.tines.com/whats-new/case-deletion)
- [Improved Records pie and bar charts ](https://www.tines.com/whats-new/improved-records-pie-and-bar-charts)
- [Action Performance API](https://www.tines.com/whats-new/action-performance-api)



### October 14th, 2024 - 26.2.0

```
build-sha: c8f85347_v26_2_0
tines/tines-app: sha256:81197015449b66d97343221059191606380a72f963e6f73a5ee9874678393db2
tines/tines-nginx: sha256:ca6c6dcf205f7c5bc4bd27e4a46f8e83f0dc8da1f16609fdbc95b8a0b34288ce
```

- [Updating Case saved view](https://www.tines.com/whats-new/updating-case-saved-view)
- [Adding `JSON_SCHEMA_VALIDATE` function](https://www.tines.com/whats-new/adding-json-schema-validate-function)
- [`IS_VALID_JSON_SCHEMA` function](https://www.tines.com/whats-new/is-valid-json-schema-function)
- [Convert Workbench chat to story](https://www.tines.com/whats-new/convert-workbench-conversation-to-story)
- [Contextual row actions for tables](https://www.tines.com/whats-new/contextual-row-actions-for-tables)
- [Send to test option](https://www.tines.com/whats-new/send-to-test-option)
- [Response time SLAs](https://www.tines.com/whats-new/response-time-sla)

### October 10th, 2024 - 26.1.3

```
build-sha: 609eed98_v26_1_3
tines/tines-app: sha256:7523203bba11982ce67721cb813e4718297e2ab02e09f3c6e47483557f40428e
tines/tines-nginx: sha256:3ee3b3229cddb837f9516478305ff2224448f7a502f48bec7e80aced1dccd1cd
```

- Fix for API Response Enabled Webhooks to explicitly check out a new ActiveRecord connection on each execution.
- Introduce environment variable `DATABASE_IDLE_TIMEOUT` with default of 300 ms before the connection should be flushed.

### October 7th, 2024 - 26.1.2

```
build-sha: 667bd483_v26_1_2
tines/tines-app: sha256:b8d03f9fc52f8bd2424ff3f621c271306724b634de037f06986e74e3a7f099e2
tines/tines-nginx: sha256:3ee3b3229cddb837f9516478305ff2224448f7a502f48bec7e80aced1dccd1cd
```

Patch for tines-command-runner alpha release impacting testing customers *only*.

- Fix to run tines-command-runner as non-root user
- Fix to allow for custom pypi-server usage

### October 1st, 2024 - 26.1.1

```
build-sha: aa2c460f_v26_1_1
tines/tines-app: sha256:eb2d7887722d5ae780b56a09c4fe82447543557cd12c0e1e8d998f472e60b49a
tines/tines-nginx: sha256:9fdd6ce4c03d51a6090a96c2a552cf6fe633797fbb4134bcc8c3825c667bc16b
```

- Fixing `tines-app:latest` tags in dockerhub. 

### September 30th, 2024 - 26.1.0

```
build-sha: 4438af06_v26_1_0
tines/tines-app: sha256:564554089922c952433690da9bb2aeec72059d4ce8b64d308b6a1ebe98380e25
tines/tines-nginx: sha256:9fdd6ce4c03d51a6090a96c2a552cf6fe633797fbb4134bcc8c3825c667bc16b
```

- [Case timeline filtering](https://www.tines.com/whats-new/case-timeline-filtering)
- [Dashboard chart color palettes](https://www.tines.com/whats-new/dashboard-chart-color-palettes)
- [Case multiple file uploads](https://www.tines.com/whats-new/case-multiple-file-uploads)
- [Add no-match links via the Actions API](https://www.tines.com/whats-new/add-no-match-links-via-the-actions-api)
- [Navigation dropdown hierarchy update](https://www.tines.com/whats-new/navigation-dropdown-hierarchy-update)
- [New list tables](https://www.tines.com/whats-new/new-list-tables)
- [New `AVERAGE` Function](https://www.tines.com/whats-new/new-average-function)
  
  

### September 16th, 2024 - 26.0.3

```
build-sha: 16ae995c_v26_0_3
tines/tines-app: sha256:81a00dd647c71fb37f8d291a8527c508356faa49d0e464b9244c3ea33906d521
tines/tines-nginx: sha256:426aeeacb1ed51704035d9f760ab175a622aed22858340a99ad109bbb66a65d2
```

- Introduce support for hosting Tines app on dedicated IP as Tenant domain with custom port. 
  
  - Renamed `TENANT_MAILER_PORT` to `TENANT_CLIENT_PORT`

### September 16th, 2024 - 25.2.6

```
build-sha: c03f2fd9_v25_2_6
tines/tines-app: sha256:7c1e6a2c1cee4a2fded09be47258301e4e87acf5ba5f139b15c13854dbc74aac
tines/tines-nginx: sha256:e5791ce200495c64b78bc0470f1c4dd11cd21968963ed3361b32abf560865aaf
```

- Introduce support for hosting Tines app on dedicated IP as Tenant domain with custom port. 
  
  - Renamed `TENANT_MAILER_PORT` to `TENANT_CLIENT_PORT`

### September 16th, 2024 - 26.0.2

```
build-sha: 90dbb8b3_v26_0_2
tines/tines-app: sha256:9a86b5a68356a2dabbbf6766f9fdbde2dccbe3d3a67efc8d99cafbf167484d0b
tines/tines-nginx: sha256:3f88c4cf9443fc8bfd411430d6a59fb66b50f4761e58dbbf6e02487037b700c3
```

- Introduce `TENANT_MAILER_PORT`  - Specify the port to be used in email links for the tenant's address (e.g., in invitation emails).

### September 16th, 2024 - 25.2.5

```
build-sha: 0847fea3_v25_2_5
tines/tines-app: sha256:f3dec97b713a8ad30c3f963d34ba80d6e89e0d2765c43347c07820741a27e5cb
tines/tines-nginx: sha256:82c9ca1cbf2e6b562f7e7d65644db7ed4a71fb8605746833515d4f4dd6a7b411
```

- Introduce `TENANT_MAILER_PORT`  - Specify the port to be used in email links for the tenant's address (e.g., in invitation emails).

### ~~September 16th, 2024 - 26.0.1 ~~[DEPRECATED IN FAVOR OF 26.0.2]

```
build-sha: 81a3954a_v26_0_1
tines/tines-app: sha256:b806cce801dbc31969956f45951d227cb392579b2e1705c10933b3918ac5f231
tines/tines-nginx: sha256:3f88c4cf9443fc8bfd411430d6a59fb66b50f4761e58dbbf6e02487037b700c3
```

- Introduce `TENANT_MAILER_PORT`  - Specify the port to be used in email links for the tenant's address (e.g., in invitation emails).

### ~~September 10th, 2024 - 25.2.4~~ [DEPRECATED IN FAVOR OF 25.2.5]

```
build-sha: 53b92fe6_v25_2_4
tines/tines-app: sha256:f99a29325c48c1e47cff278f12f4042c9fd4067446300f2ce7efc467704c04e0
tines/tines-nginx: sha256:1e1af50e8276ed39ba4ad1e41cd7c492a02ba0d6eb16bedce81a07a0c4a854b8
```

- Introduce `TENANT_MAILER_PORT`  - Specify the port to be used in email links for the tenant's address (e.g., in invitation emails).

### September 16th, 2024 - 26.0.0

```
build-sha: 569312b6_v26_0_0
tines/tines-app: sha256:2a3ff767c93d714c1b622116e2a40f4ea4630c50a86ac042c184f1498cb83416
tines/tines-nginx: sha256:3f88c4cf9443fc8bfd411430d6a59fb66b50f4761e58dbbf6e02487037b700c3
```

> **NOTE:**
> Please space out your last upgrade from 25.2.x to 26.0.0 by at least one week. We are shipping some background maintenance to improve how action runs and action run-specific data modeling are performed. As part of this maintenance, we have shipped some tasks that backfill the data behind the scenes without any impact on action runs.
> 
> To ensure a smooth transition, we request that you allow at least one week between your last upgrade on 25.2.x and the upgrade to 26.0.0.

- [Linking Cases through Records API and Record action](https://www.tines.com/whats-new/linking-cases-through-records-api-and-capture-record-action)
- [Meet Tines Workbench](https://www.tines.com/whats-new/meet-tines-workbench)
- [New action icons](https://www.tines.com/whats-new/new-action-icons)
- [Togglable Cases sidebars](https://www.tines.com/whats-new/togglable-cases-sidebars)
- [Search action options](https://www.tines.com/whats-new/search-action-options)
- [New dashboard chart options](https://www.tines.com/whats-new/new-dashboards-chart-options)
- [Case fields](https://www.tines.com/whats-new/case-fields)
- [New Source Field Added to Audit Logs Table](https://www.tines.com/whats-new/new-source-field-added-to-audit-logs-table)
- [Credential metadata overview](https://www.tines.com/whats-new/credential-metadata-overview)
- [`ESTIMATED\_TOKEN\_COUNT` function](https://www.tines.com/whats-new/estimated-token-count-function)
- [Temperature option in AI Action](https://www.tines.com/whats-new/temperature-option-in-ai-action)
- [Create / Update the test details of a credential via API](https://www.tines.com/whats-new/create-update-test-details-credential-api)
- [Manage your user profile](https://www.tines.com/whats-new/manage-your-user-profile)
- [SCIM user group mapping API](https://www.tines.com/whats-new/scim-user-group-mapping-api)
- [Cases Board view](https://www.tines.com/whats-new/cases-board-view)
- [Action egress control rules](https://www.tines.com/whats-new/action-egress-control-rules)
- [View and search your credentials, resources, API keys, and templates in a table](https://www.tines.com/whats-new/view-and-search-your-credentials-resources-api-keys-and-templates-in-a-table)
- [Simultaneously invite users to the tenant and teams](https://www.tines.com/whats-new/simultaneously-invite-users-to-the-tenant-and-teams)
- [Switching teams from the command bar](https://www.tines.com/whats-new/switching-teams-from-the-command-palette)
- [Show presence for multiple active users](https://www.tines.com/whats-new/show-presence-for-multiple-active-users)
- [Custom case SLA time frames](https://www.tines.com/whats-new/custom-case-sla-time-frames)
- [API support for IP access control rules](https://www.tines.com/whats-new/api-support-for-ip-access-control-rules) 
- Bug fixes

### September 10th, 2024 - 25.2.3

```
build-sha: cf9cb679_v25_2_3
tines/tines-app: sha256:6ef89b3d0b919936b5e16acb35f434f9fc2ed06114632014b7d88595734c4a79
tines/tines-nginx: sha256:51523b2452cb16d74cedcd811cb764e7d9f02f613336be74198a4d35e2a83a86
```

- Fixes FIPS issues with attachments from 25.2.2

### September 10th, 2024 - 25.2.2

```
build-sha: 27cb257c_v25_2_2
tines/tines-app: sha256:659eca044b43b47ddfa8ee7bd1d63f993cc7e662b4c499071b9b0a2e2c4604aa
tines/tines-nginx: sha256:b15e468fd4e87f65c9fe4f9a9c98307204276ac580a00a28766a31482b813aca
```

- Fixes FIPS issues with attachments

### September 10th, 2024 - 25.2.1

```
build-sha: 9fe5ea93_v25_2_1
tines/tines-app: sha256:5f99a2bdba3e82f4bf69750cbd8c7af1aaf5abad230cee4f911cd703418d28a1
tines/tines-nginx: sha256:a05a85127b403db064f13d1619b709fe2cdff763d7ea1a3f952e6f4f10cf28c0
```

- Security update

### September 10th, 2024 - 24.3.1

```
build-sha: cc1cbbce_v24_3_1
tines/tines-app: sha256:75853551fbda730be8748e757c4f06554262ee2c847c8fe2a39784cc9a01b9ac
tines/tines-nginx: sha256:e6af75f0b664329ecff8271de8e7078c06c5ac562b4c141c0ff2cab8bd5c626c
```

- Security update

### September 2nd, 2024 - 25.2.0

```
build-sha: 65a88b51_v25_2_0
tines/tines-app: sha256:5516e381d8b5980ba16fb966b97f0876c05d2ceb60433d3c751d42c0a21ad2a2
tines/tines-nginx: sha256:a67cae820330be4f374582bf3685d70e93dacabe7c1e32e81d57a8b7984e63be
```

- [Case Fields](https://www.tines.com/whats-new/case-fields)
- [New Source Field Added to Audit Logs Table](https://www.tines.com/whats-new/new-source-field-added-to-audit-logs-table)
- [Credential metadata overview](https://www.tines.com/whats-new/credential-metadata-overview)
- [`ESTIMATED_TOKEN_COUNT` function](https://www.tines.com/whats-new/estimated-token-count-function)

### August 19th, 2024 - 25.1.0

```
build-sha: 37f5ab38_v25_1_0
tines/tines-app: sha256:49e9456348151100aa3a053c88d2e2bd51538cd396ff81a7661aa5d05c41890c
tines/tines-nginx: sha256:3e7cfbfe5fb2c5c50166f8497e9e7fb375fe33236354956a1095acfe2d041461
```

- [Create / Update the test details of a credential via API](https://www.tines.com/whats-new/create-update-test-details-credential-api)
- [Manage your user profile](https://www.tines.com/whats-new/manage-your-user-profile)
- [SCIM user group mapping API](https://www.tines.com/whats-new/scim-user-group-mapping-api)
- [Cases Board view](https://www.tines.com/whats-new/cases-board-view)
- [Action egress control rules](https://www.tines.com/whats-new/action-egress-control-rules)
- [View and search your credentials, resources, API keys, and templates in a table](https://www.tines.com/whats-new/view-and-search-your-credentials-resources-api-keys-and-templates-in-a-table)
- [Simultaneously invite users to the tenant and teams](https://www.tines.com/whats-new/simultaneously-invite-users-to-the-tenant-and-teams)

### August 5th, 2024 - 25.0.0

```
build-sha: 468ec387_v25_0_0
tines/tines-app: sha256:def49943f355ba3fd8f74e3a767d56c198f9ce632010377916b344a26b6231b8
tines/tines-nginx: sha256:467169da2f18e832aff169c6ff5b166357025ac1d1c93cbfd4dd890cf2c729ad
```

- [Case comments searching](https://www.tines.com/whats-new/case-comments-searching)
- [Custom case SLA time frames](https://www.tines.com/whats-new/custom-case-sla-time-frames)
- [Show presence for multiple active users](https://www.tines.com/whats-new/show-presence-for-multiple-active-users)
- [Switching teams from the command bar](https://www.tines.com/whats-new/switching-teams-from-the-command-palette)
- Fixes an issue related to modifying JIT settings

### July 22nd, 2024 - 24.3.0

```
build-sha: f2fa9091_v24_3_0
tines/tines-app: sha256:db930fd50861359709173fe5d60b849be0bdb830e68eca5dff3c9b45e42a853a
tines/tines-nginx: sha256:70be1586a8764571c5e90a834eca25f0be1d0c595c96ceece7610540719bfcba
```

- [Support regex and custom response for webhook actions with rules](https://www.tines.com/whats-new/support-regex-and-custom-response-for-webhook-action-rules)
- [Cases: table of contents](https://www.tines.com/whats-new/cases-table-of-contents)
- [Resource locking](https://www.tines.com/whats-new/resource-locking)

### July 8th, 2024 - 24.2.0

```
build-sha: 1e58c8ee_v24_2_0
tines/tines-app: sha256:da8f018cf9217feade5774a234bd6cc7915fdfd280cafb6ceb6730f737e2d1e9
tines/tines-nginx: sha256:28d36f2dbe5913016f03d7d8793b815b1bd6a806c405343b6b66ebab2cab8aab
```

- [Case Groups](https://www.tines.com/whats-new/case-groups)
- [Granular Cases permissions](https://www.tines.com/whats-new/granular-cases-permissions)
- [Webhook Rules to pre-filter events based on request body or headers ](https://www.tines.com/whats-new/webhook-rules-to-pre-filter-events-based-on-request-body-or-headers)
- [Restrict access to pages with SSO groups ](https://www.tines.com/whats-new/control-access-to-pages-with-sso-groups)
- [Support for logos in page collection headers ](https://www.tines.com/whats-new/support-for-logos-in-page-collection-headers)
- [Defined send to story parameters ](https://www.tines.com/whats-new/defined-send-to-story-parameters)

### June 24th, 2024 - 24.1.0

```
build-sha: d3a8cfe6_v24_1_0
tines/tines-app: sha256:1adb4401773aa6829bdb2f55c38d77dc4cca3c5d03cf78b8f63487a077735d63
tines/tines-nginx: sha256:7ebbc28af9f151444d961d9842ca4f604201b8974e6e0e956ca7d4dcc33b5c29
```

- [New event panel buttons](https://www.tines.com/whats-new/new-event-panel-buttons)
- [New navigation experience](https://www.tines.com/whats-new/new-navigation-experience)
- [Image support in AI actions](https://www.tines.com/whats-new/image-support-in-ai-actions)
- [Enhanced JIT (just-in-time) login syncing](https://www.tines.com/whats-new/enhanced-jit-just-in-time-login-syncing)
- [New user session expiry UI](https://www.tines.com/whats-new/new-user-session-expiry)
- [Get the raw message in the Receive Email action](https://www.tines.com/whats-new/get-the-raw-message-in-the-receive-email-action)
- [Meet the AI Action](https://www.tines.com/whats-new/meet-the-ai-action)

### June 12th, 2024 - 24.0.1

```
build-sha: 86420444_v24_0_1
tines/tines-app: sha256:97e2af5c8fb8dc737923e687d946bc38431f1fa84349abd8ad75493932b1d3f2
tines/tines-nginx: sha256:cae43f212f395cd61682d93a6bb3ce86f640b0856c180a8ca29b6700a854a04b
```

- Improves stability of upgrades in cases of failed migrations

### June 12th, 2024 - 23.3.3

```
build-sha: 518392f0_v23_3_3
tines/tines-app: sha256:b9062ca20e9c684575cd7be89f623051bac0213bf834701e9339a72e8e48f81d
tines/tines-nginx: sha256:5234be634ca56eff5943a036666f414f3927ad433103bc82d4fcd940869ca4ca
```

- Improves stability of upgrades in cases of failed migrations

### June 10th, 2024 - 24.0.0

```
build-sha: 4ec38bf7_v24_0_0
tines/tines-app: sha256:6cb8ef9c0c71f11a708da602945ec9cdff124acb28b42c3037d0a0ffab47a559
tines/tines-nginx: sha256:cae43f212f395cd61682d93a6bb3ce86f640b0856c180a8ca29b6700a854a04b
```

- [New multi-request credential type](https://www.tines.com/whats-new/new-multi-request-credential-type)

### May 30th, 2024 - 23.3.2

- Bug fixes related to modal scrolling

### ~~May 30th, 2024 - 23.3.1~~ [Deprecated in favor of 23.3.2]

### May 29th, 2024 - 23.3.0

```
build-sha: 6ed9ad69_v23_3_0
tines/tines-app: sha256:630937a1253bbbcff82b49ec3130b7ba782a77ede124dd88ebd3c7721d30123d
tines/tines-nginx: sha256:4542c4c58c3c0cba4b83a73fff74edd1c2a77652d437144325195312c09af044
```

- [Dashboards for Records](https://www.tines.com/whats-new/dashboards-available-to-records-customers)
- openSSL  3 support for Tines FIPS only image

### May 27th, 2024 - 23.2.0

```
build-sha: 2d461886_v23_2_0
tines/tines-app: sha256:3fc161d9cff6ba3d6d37701f816b33cdd05269015551e153596ac51ec442550d
tines/tines-nginx: sha256:4542c4c58c3c0cba4b83a73fff74edd1c2a77652d437144325195312c09af044
```

- [Tunnel health checks and metrics](https://www.tines.com/whats-new/tunnel-health-checks-metrics)
- [Create beautiful images of your dashboards](https://www.tines.com/whats-new/dashboard-export)
- [Case SLAs](https://www.tines.com/whats-new/case-slas)
- [Restrict direct credential access](https://www.tines.com/whats-new/restrict-direct-credential-access)
- [Custom case statuses](https://www.tines.com/whats-new/custom-case-statuses)
- [Password field for pages](https://www.tines.com/whats-new/password-field-for-pages)

### May 22nd, 2024 - 23.1.4

```
build-sha: 3c8a71d0_v23_1_4
tines/tines-app: sha256:7800247e4cac5694936833dea7e152bfef8f9cc9ed9a77ceabb42319277a25b3
tines/tines-nginx: sha256:7ed684d5a7cca55724e8dbc4a9299b598c10d7ab77eee43b62ea834283f273bc
```

- Bug fix during upgrade in 23.1.x
- Please note that version 23.1.3 was intentionally skipped due to issues identified during the patch release process

### May 16th, 2024 - 23.1.2

```
build-sha: 3918c276_v23_1_2
tines/tines-app: sha256:77ce0d863833123f7747b2c21d2bd85116742eca0a1d4e36b104aa7b3d16cb37
tines/tines-nginx: sha256:acf5df26299c91ccdcf694be8bff3f74c6c60c756dc5e401a327e8400bf82d4e
```

- Bug fix during upgrade in 23.1.x

### May 13th, 2024 - 23.1.1

```
build-sha: afbea907_v23_1_1
tines/tines-app: sha256:964624237f7695b88d0d1ef215c20f005b5c4d4a271f53e31735a9ae22c46434
tines/tines-nginx: sha256:acf5df26299c91ccdcf694be8bff3f74c6c60c756dc5e401a327e8400bf82d4e
```

- OpenSSL bug fix and introduce new var to regular openSSL Sec Levels via `OVERRIDE_OPENSSL_SECLEVEL`. Documentation: [https://www.tines.com/docs/self-hosting/resolving-openssl-3-upgrade-issues](https://www.tines.com/docs/self-hosting/resolving-openssl-3-upgrade-issues)

### May 13th, 2024 - 23.0.4

```
build-sha: 653f494c_v23_0_4
tines/tines-app: sha256:29a0c4807ed1443c64b6c6b2a92196a37a451631215491d64a522dac170837f0
tines/tines-nginx: sha256:aea8934d4615be12a4485ff32ae8f5aeece5971405ebeebfb35b6f9c8c8db63c
```

- OpenSSL bug fix and introduce new var to regular openSSL Sec Levels via `OVERRIDE_OPENSSL_SECLEVEL`. Documentation: [https://www.tines.com/docs/self-hosting/resolving-openssl-3-upgrade-issues](https://www.tines.com/docs/self-hosting/resolving-openssl-3-upgrade-issues)

### May 13th, 2024 - 23.1.0

```
build-sha: 36710205_v23_1_0
tines/tines-app: sha256:7e027f0c216c32b595f6c204c910b2a623a8daa8d81f8eff51a3e402ff127cf8
tines/tines-nginx: sha256:2cd1b5665e7c8fbe577cae0d494f773fc281a5a488efa6b66d12cfdba0abbc5f
```

- Bug fixes
- Domain restricted credentials now support URL paths
- Domain restricted credentials now support IMAP actions
- Reorder pages in a collection

### May 3rd, 2024 - 23.0.3

```
build-sha: 36bd16c4_v23_0_3
tines/tines-app: sha256:0d38166dce4a33f89f2a52952f5d4fa102f51de389103d56e8f5e987ada5c950
tines/tines-nginx: sha256:d095e45c1fc029841b407a1a074ade1f9aa46b9c0e62f26234da592e07be9537
```

- Bug fixes

### May 1st, 2024 - 23.0.2

```
tines/tines-app: sha256:6ac85b9a613a5cbc9a1e16431d2992b9c45a542a745658d6cd42265f95dc12e0
tines/tines-nginx: sha256:d095e45c1fc029841b407a1a074ade1f9aa46b9c0e62f26234da592e07be9537
```

- Bug fix

### April 30th, 2024 - 23.0.1

```
tines/tines-app: sha256:8e414a3f04442cc9b67cda04e98262cfeec1b8343ab257a6bf8cd0f896395bc1
tines/tines-nginx: sha256:d095e45c1fc029841b407a1a074ade1f9aa46b9c0e62f26234da592e07be9537
```

- Bug fix to startup script

### April 26th, 2024 - 23.0.0

```
tines/tines-app: sha256:bb29e0eea60c9731ff323d55d5ecb6dbae2d5888c0ac57cfeb807a1c403280d9
tines/tines-nginx: sha256:fb4f946fbf6e4169436bea75c6c73fc4dac7d596eb0c6522e6354859114f37f3 
```

> ✏️ **Note**: Starting with `v23.0.0`, Tines now offers two images. One is the standard edition that you have been using, known as `tines-app` and the other is a new image that is FIPS compatible. There are no changes in features or application logic. The only differences between these two images are the underlying version of OpenSSL and support for TLS 1.3. If you are using Tines with FIPS enabled (via the `RUN_FIPS=true` environment variable), please refer to our [documentation](https://www.tines.com/docs/self-hosting/running-with-openssl-fips) for further instructions. Otherwise, you do not need to make any changes on your end.

- [Case templates](https://www.tines.com/whats-new/case-templates)
- [Creating records from a case](https://www.tines.com/whats-new/creating-records-from-a-case)
- [New tunnel health statuses in UI and API](https://www.tines.com/whats-new/new-tunnel-health-statuses-in-ui-and-api)
- [`raw` `endraw` formula tags](https://www.tines.com/whats-new/raw-endraw-formula-tags)
- [Functions for hybrid RSA/AES encryption](https://www.tines.com/whats-new/functions-for-hybrid-rsa-aes-encryption)
-  [More mention types in cases and change requests](https://www.tines.com/whats-new/more-mention-types-in-cases-and-change-requests)

### April 23rd, 2024 - 22.3.4

```
tines/tines-app: sha256:a6e610c1efab50880aa4403f57bb09682376eaacd5550dc3e542f09e972d249b
tines/tines-nginx: sha256:0eb090c7d1b7caf2d30b06de6c3745643459cde7f8c92a95ee6f2d74a4706e63
```

- Redirect bug fix for fargate setups

### April 19th, 2024 - 22.3.3

```
tines/tines-app: sha256:da2b16662a26370ed8b2a37dbf3ace7e72932b7fe2a6f99378d93374c53f1196
tines/tines-nginx: sha256:0b978a7b922327871b39c2a650bf8e16e50ef1e67304a042e268b82e44b975a5
```

- Extend default `SMTP_OPEN_TIMEOUT` and `SMTP_READ_TIMEOUT`to 30 seconds

### April 18th, 2024 - 22.3.2

```
tines/tines-app: sha256:4bd7d92282811a5684827f591c29091e734ec1cabc413508f4c8e3f7c615996e
tines/tines-nginx: sha256:0b978a7b922327871b39c2a650bf8e16e50ef1e67304a042e268b82e44b975a5
```

- Support for `SMTP_OPEN_TIMEOUT` and `SMTP_READ_TIMEOUT` so you can set custom timeout settings for SMTP connections

### April 16th, 2024 - 22.2.6

```
tines/tines-app: sha256:e33f9db50dbd48c13ef3cf024e9716771cb9f167eea121a0ebbd5925209ce980
tines/tines-nginx: sha256:e17156247763f7956be8c1e1c718bbd5f2710f136d549110f13219bcd4d78ac5
```

- Bug fixes

### April 15th, 2024 - 22.3.1

```
tines/tines-app: sha256:89835e203835b4be3bcf5b716c42aabaa9638a0392121b1295495d879a80ac20
tines/tines-nginx: sha256:e17156247763f7956be8c1e1c718bbd5f2710f136d549110f13219bcd4d78ac5ea79f3f0c6dd9a
```

- [Functions for hybrid RSA/AES encryption](https://www.tines.com/whats-new/functions-for-hybrid-rsa-aes-encryption)
- [More mention types in cases and change requests](https://www.tines.com/whats-new/functions-for-hybrid-rsa-aes-encryption)

### ~~April 15th, 2024 - 22.3.0 ~~[Deprecated in favor of 22.3.1]

### April 10th, 2024 - 22.2.5

```
tines/tines-app: sha256:278f453e12fb7163a0fcd3ef8d767f892469fcf4e04fed677c4b7534299c308d
tines/tines-nginx: sha256:2780dbc5350aedb834580b61a930265528ff3b075033680415393765fea79f3f0c6dd9a
```

- Fix for Postgres users without `CREATEDB` permissions during upgrades.

### April 6th, 2024 - 22.2.4

```
tines/tines-app: sha256:e5750f9659524be58fdb090a51fa2862246be5836bd31006227bec2f8f987656
tines/tines-nginx: sha256:0f49b316c4da873b35a465771097b9ee7064772b69585c72e9463778c0c6dd9a
```

- Bug fixes
- Minor background job performance improvements

### ~~April 5th, 2024 - 22.2.3~~ [Deprecated in favor of 22.2.4]

- Skip. Has a bug in nginx config.

### April 2nd, 2024 - 22.2.2

```
tines/tines-app: sha256:95a185050b200594c285bb9890cbfb945aac48521abe16f521cf6bb9b75f9468
tines/tines-nginx: sha256:f6a95374ebb22a63bb0a0c60b80dc0461cbe844c0c3cf81ed83f18df3415fae2
```

- Bug fix for nginx startup

### April 2nd, 2024 - 22.2.1

```
tines/tines-app: sha256:f9ba93db324e9424af29b858a16a8b6b71bf083defb24f78acf4fbfbe520499f
tines/tines-nginx: sha256:ac6d48ebeee6e25d0dbb8d1cad5dff7a41e1d7d3b208f521e5bb503065319363
```

- Performance fix for a background job
- Introduce optional `TINES_NGINX_PROXY_REDIRECT_OFF` ENV variable for self hosted setups running nginx. 
  
  - This allows you set `proxy_redirect off;` for nginx installations that need it.

### April 1st, 2024 - 22.2.0

```
tines/tines-app: sha256:b7f1156e459eb69baa1a540a74c694c06914c656d3feecc79c13d89f2693e65f
tines/tines-nginx: sha256:acd3dac46ff53e695eff3d15ab540fd25e7913d0c5b60a3c671c3691f260a512
```

- Removed deprecated `PARQUET_PARSE` function
- [Configurable Login Notice](https://www.tines.com/whats-new/configurable-login-notice)
- [Case actions improvements](https://www.tines.com/whats-new/case-actions-improvements)
- [Response time reporting in HTTP Request Action](https://www.tines.com/whats-new/response-time-reporting-in-http-request-action)
- [User authentication session updates](https://www.tines.com/whats-new/user-authentication-session-updates)
- [Dive Deep with the new Action Performances Tab](https://www.tines.com/whats-new/dive-deep-with-the-new-action-performances-tab)
- [Expiring records](https://www.tines.com/whats-new/records-ttl)
- [Large text Record field type](https://www.tines.com/whats-new/record-artifacts)
- [Custom profile pictures](https://www.tines.com/whats-new/custom-profile-pictures)
- [Case MTTR/MTTA on dashboards](https://www.tines.com/whats-new/case-mttr-mtta-on-dashboards)
- [See who else is actively present on a Case with you](https://www.tines.com/whats-new/see-who-else-is-actively-present-on-a-case-with-you)
- [Our API now supports groups](https://www.tines.com/whats-new/group-api-support)
- [Case comment reactions](https://www.tines.com/whats-new/case-comment-reactions)
- [HTTP Request Action retries without notification](https://www.tines.com/whats-new/http-request-action-retries-without-notification)
- [Text validator functions and `MIME_HEADER_DECODE`](https://www.tines.com/whats-new/text-validator-functions)
- [Case list relative time filtering](https://www.tines.com/whats-new/case-list-relative-time-filtering)

### March 20th, 2024 - 22.1.1

```
tines/tines-app: sha256:515838994d8fc13a69804479b0a750b44a2c7015ee20cd0252bd0c4b92587090
tines/tines-nginx: sha256:e3ae30e35e57726859d03654974880a0607e46abde5d5657681c180fb80baf4f
```

- Support for `NO_PROXY`* *when* *`HTTP_PROXY` is present in the environment

### March 18th, 2024 - 22.1.0

```
tines/tines-app: sha256:f4d6a41c2e4afad107fa79d7fc360e1c5a6841df627b05353d9361363680a211
tines/tines-nginx: sha256:e3ae30e35e57726859d03654974880a0607e46abde5d5657681c180fb80baf4f
```

- [Rename actions inline ](https://www.tines.com/whats-new/rename-actions-inline)
- [Case list relative time filtering](https://www.tines.com/whats-new/case-list-relative-time-filtering)
- [Text validator functions and `MIME_HEADER_DECODE`](https://www.tines.com/whats-new/text-validator-functions)
- [HTTP Request Action retries without notification](https://www.tines.com/whats-new/http-request-action-retries-without-notification)
- [Case comment reactions](https://www.tines.com/whats-new/case-comment-reactions)
- [Our API now supports groups](https://www.tines.com/whats-new/group-api-support)
- [See who else is actively present on a Case with you](https://www.tines.com/whats-new/see-who-else-is-actively-present-on-a-case-with-you)

### March 13th, 2024 - 22.0.2

```
tines/tines-app: sha256:f40f5d2a02d3975e79d22ebc8fc6650a9d12f532faf76e5254e430167b5e49d1
tines/tines-nginx: sha256:38d61c0b88222cbc38e6676e80f3557566218df5172cbd9fe5ce4ef459722948
```

- Fix incorrect version in `/version` and `/info` endpoints.
- The `PARQUET_PARSE` formula as been deprecated in version 22

### ~~March 6th, 2024 - 22.0.1~~ [Deprecated in favor of 22.0.2]

```
tines/tines-app: sha256:5ae80c15118b30d94e54499b8afc1d70d04f510e57e4bc3977e741794fabdc73
tines/tines-nginx: sha256:d47b72319e789f7e53e48070cff5161a4df1e1d85a7171b98fd467939d618725
```

- Bug fix for GraphQL queries
- Bug fix for pending runs of deleted stories
- The `PARQUET_PARSE` formula as been deprecated in version 22

Note: this version may incorrectly report "20.0.1" as the current version in the `/version` and `/info` endpoints.

### March 6th, 2024 - 21.3.1

```
tines/tines-app: sha256:4851fd91949dd68da69d16fa779e7e1630f11c572bdea6de630095a3098e2b77
tines/tines-nginx: sha256:d47b72319e789f7e53e48070cff5161a4df1e1d85a7171b98fd467939d618725
```

- Bug fix for GraphQL queries
- Bug fix for pending runs of deleted stories

### ~~March 4th, 2024 - 22.0.0~~ [Deprecated in favor of 22.0.2]

```
tines/tines-app: sha256:9e68e3db8a8119c043a8790fac3435b6c779b6ae8776445d87b8504f9ba2cf80
tines/tines-nginx: sha256:d47b72319e789f7e53e48070cff5161a4df1e1d85a7171b98fd467939d618725
```

- [Interval option for throttle mode action](https://www.tines.com/whats-new/interval-option-for-throttle-mode-action)
- [Favorite templates](https://www.tines.com/whats-new/favorite-templates)
- [Sort cases by created](https://www.tines.com/whats-new/sort-cases-by-created)
- [Mention users in cases and change requests](https://www.tines.com/whats-new/mention-users-in-cases-and-change-requests)
- [Custom proxy url for HTTP Request Actions](https://www.tines.com/whats-new/custom-proxy-url-for-http-request-actions)

### ~~February 19th, 2024 - 21.3.0~~ [Deprecated in favor of 21.3.1]

```
tines/tines-app: sha256:4d9395876c3919ba06b99369971c5b1fb2d62acf44aa938b4ee8c7a22209dd89
tines/tines-nginx: sha256:efa27a33d1dfd8d76073c9a8ed48a4411f63f6616d89d50fab13fa3889c3e1ed
```

- [Template favouriting](https://www.tines.com/whats-new/template-favoriting)
- [Case closure requirements](https://www.tines.com/whats-new/case-closure-requirements)
- SMTP configuration and testing enhancements

### February 9th, 2024 - 21.2.0

```
tines/tines-app: sha256:34ca39dd3dc76ed299cb52b1b226a813e170717d456279c4cd88030794798ea7
tines/tines-nginx: sha256:4b8e273ada03f5404bb756df34dc71e8ce552a8f188e347ac5a0fb354adeb167
```

- [Introducing the new 'TALLY' function](https://www.tines.com/whats-new/tally-function)
- [100% increase of total number of events than an action can throttle](https://www.tines.com/whats-new/increased-capacity-for-throttle-mode-event-transformation-actions)
- [Records improvements](https://www.tines.com/whats-new/records-improvements-roundup)
- [SCIM user provisioning](https://www.tines.com/whats-new/scim-user-provisioning)
- [Export case as PDF](https://www.tines.com/whats-new/export-case-as-pdf)
- [Restrict tunnel access by team](https://www.tines.com/whats-new/restrict-tunnel-access-by-team)
- [Reorder record fields](https://www.tines.com/whats-new/reorder-record-fields)
- [Records fixed values result type](https://www.tines.com/whats-new/records-fixed-values-result-type)
- [Introducing editable records](https://www.tines.com/whats-new/editable-records)
- [Export cases as CSV](https://www.tines.com/whats-new/export-cases-as-csv)
- [Linking & unlinking records via the API](https://www.tines.com/whats-new/linking-unlinking-records-via-the-api)
- Dockerfile updated

### January 23, 2024 - 21.1.1

```
tines/tines-app: sha256:0053e008f9cea64500c2e92ea896cdc923e7568bc270152bfc49d12bdb145770
tines/tines-nginx: sha256:e7b637c57cfdfa9922f827f0bee2e9a59837628416fe77d5905372f190fec4f4
```

- [Records table column rearranging](https://www.tines.com/whats-new/records-table-column-rearranging)
- [Template search shortcut](https://www.tines.com/whats-new/template-search-shortcut)
- [Records table improvements](https://www.tines.com/whats-new/records-table-improvements)
- [New template navigator](https://www.tines.com/whats-new/new-template-navigator)
- [Discover downstream referencing Actions quickly](https://www.tines.com/whats-new/discover-downstream-referencing-actions-quickly)
- [Subscribe to a story](https://www.tines.com/whats-new/subscribe-to-a-story)
- [Tag search in cases list: 'EXCLUDE' logical operator](https://www.tines.com/whats-new/tag-search-in-cases-list-exclude-logical-operator)
- [Records table: customizable number of rows](https://www.tines.com/whats-new/change-record-table-row-size)
- [Additional case bulk actions](https://www.tines.com/whats-new/additional-case-bulk-actions)
- [Multiple Custom Certificate Authorities is here](https://www.tines.com/whats-new/multiple-custom-certificate-authorities-is-here)
- Dockerfile updated

### ~~January 22, 2024 - 21.1.0~~ [Deprecated in favor of 21.1.1]

This version was skipped. Please upgrade to 21.1.1 instead.

### January 11, 2024 - 21.0.2

```
tines/tines-app: sha256:9bd4ded160b65ea6cb2e2b037aff119e1b964d6febb960950583e9ce9e43ba94
tines/tines-nginx: sha256:7918865336ec0dbfc2292fcd82cecc60d9cace83088f3baabcbc2f3bd6b11896
```

- Bug fix for SMTP startup check

### ~~January 11, 2024 - 21.0.1~~ [Deprecated in favor of 21.0.2]

```
tines/tines-app: sha256:f2c6356787f729a8b9e922cf2d906962889590476d12ec7677c6153c147be7de
tines/tines-nginx: sha256:7918865336ec0dbfc2292fcd82cecc60d9cace83088f3baabcbc2f3bd6b11896
```

- Records API performance improvements

### ~~January 8, 2024 - 21.0.0~~ [Deprecated in favor of 21.0.2]

```
tines/tines-app: sha256:b3cd73f8a2ec221046c09c96154e11503a2c5f21444ee8e1458ba92d6ae8ce8a
tines/tines-nginx: sha256:7918865336ec0dbfc2292fcd82cecc60d9cace83088f3baabcbc2f3bd6b11896
```

- [Date and time field for pages](https://www.tines.com/whats-new/date-and-time-field-for-pages)
- [Customize an action’s event output](https://www.tines.com/whats-new/customize-an-actions-event-output)
- [Case keyboard shortcuts](https://www.tines.com/whats-new/case-keyboard-shortcuts)

### December 26, 2023 - 20.3.0

```
tines/tines-app: sha256:e65739efbd14289a6dfca478348b88d0a7cfa555d0f011090a7259a29be3103e
tines/tines-nginx: sha256:0690ba51454a239c0ffa397ab3f40fff47645e353ac6092410c36ea6ff62f08f
```

- [Improved Tines templates](https://www.tines.com/whats-new/improved-tines-templates)
- [List action logs API](https://www.tines.com/whats-new/list-action-logs-api)
- [Discover Which Actions Reference Your Resources via API](https://www.tines.com/whats-new/discover-which-actions-reference-your-resources-via-api)
- [Multiple page collections per page](https://www.tines.com/whats-new/multiple-page-collections-per-page)
- [Story runs API](https://www.tines.com/whats-new/story-runs-api)
- [Sign in with Microsoft](https://www.tines.com/whats-new/sign-in-with-microsoft)
- [`IS_PRESENT` function](https://www.tines.com/whats-new/is-present-function)
- [Improved Records pagination](https://www.tines.com/whats-new/improved-records-pagination)
- Support for `SEED_EMAIL_PASSWORD` : When setting up Tines for the first time, you can set `SEED_EMAIL_PASSWORD` in your environment variable. This will bypass the email invite process  for the first user and allow the `SEED_EMAIL` to login without SMTP configured.
- New UI for testing SMTP connection. Navigate to `/admin/configuration` to access the new UI designed for evaluating and troubleshooting your SMTP configuration.

### December 11, 2023 - 20.2.0

```
tines/tines-app: sha256:6b8112c6694ac8334dab4a7cbd49537daa5fd24dfb89b4be807644589285533d
tines/tines-nginx: sha256:d6aa278eb7edae70602cc3312db26796c17290c592393877c4d8247bfe61e073
```

- [Test resources for change control](https://www.tines.com/whats-new/test-resources-for-change-control)
- [Notification settings](https://www.tines.com/whats-new/notification-settings)
- [`IF_ERROR` function](https://www.tines.com/whats-new/is-error-function)
- [Stacked area charts](https://www.tines.com/whats-new/stacked-area-charts)
- [Additional case filters](https://www.tines.com/whats-new/additional-case-filters)
- [Events search toggle order (asc/desc)](https://www.tines.com/whats-new/events-search-toggle-order-asc-desc)
- [Custom sender email address management](https://www.tines.com/whats-new/custom-sender-email-address-management)
- [Instantly disable story](https://www.tines.com/whats-new/instantly-disable-story)
- [Test-mode credentials for change control](https://www.tines.com/whats-new/test-credentials)
- [Cases dashboards](https://www.tines.com/whats-new/cases-dashboards)
- [Better search for stories, resources, cases, and more](https://www.tines.com/whats-new/better-search-for-stories-resources-cases-and-more)
- [Record types management UI](https://www.tines.com/whats-new/record-types-management-ui)
- [Additional case priorities](https://www.tines.com/whats-new/additional-case-priorities)
- [Case buttons: open pages in new window](https://www.tines.com/whats-new/case-buttons-open-pages-in-new-window)
- [In-product notifications for cases](https://www.tines.com/whats-new/in-product-notifications-for-cases)
- [Change control navigation improvement](https://www.tines.com/whats-new/change-control-navigation-improvement)
- [Team-level API keys](https://www.tines.com/whats-new/team-level-api-keys)
- [Records API pagination limit increase](https://www.tines.com/whats-new/records-api-pagination-limit-increase)
- [Secret value within HTTP request credentials](https://www.tines.com/whats-new/secret-value-within-http-request-credentials)
- [All Send to Story links at a glance](https://www.tines.com/whats-new/all-send-to-story-links-at-a-glance)
- [Events search improvements](https://www.tines.com/whats-new/events-search-improvements)
- [Wrap long lines in Markdown code blocks](https://www.tines.com/whats-new/wrap-long-lines-in-markdown-code-blocks)
- [Credential metadata](https://www.tines.com/whats-new/credential-metadata)
- [‘Formula is false’ trigger rule](https://www.tines.com/whats-new/formula-is-false-trigger-rule)
- [Expanded JSON builder](https://www.tines.com/whats-new/expanded-json-builder)
- [Story Versions API](https://www.tines.com/whats-new/story-versions-api)

### November 27, 2023 - 20.1.0

```
tines/tines-app: sha256:645310734ccbd2af2aabe7dac7074dad2517318768a44da4d7dd08c987361dab
tines/tines-nginx: sha256:b750bce1ea4390ff1e58b3ef89dbc7c4fedc911f69bd87db7c40f77263ae7c16
```

- [Throttle improvements](https://www.tines.com/whats-new/throttle-improvements)
- [Story versions API](https://www.tines.com/whats-new/story-versions-api)
- [Expanded JSON builder](https://www.tines.com/whats-new/expanded-json-builder)
- ['Formula is false' trigger rule](https://www.tines.com/whats-new/formula-is-false-trigger-rule)
- [Credential metadata](https://www.tines.com/whats-new/credential-metadata)
- [Wrap long lines in Markdown code blocks](https://www.tines.com/whats-new/wrap-long-lines-in-markdown-code-blocks)
- [Events search improvements](https://www.tines.com/whats-new/events-search-improvements)
- [All Send to Story links at a glance](https://www.tines.com/whats-new/all-send-to-story-links-at-a-glance)
- [Secret value within HTTP request credentials](https://www.tines.com/whats-new/secret-value-within-http-request-credentials)
- [Records API pagination limit increase](https://www.tines.com/whats-new/records-api-pagination-limit-increase)
- [Team-level API keys](https://www.tines.com/whats-new/team-level-api-keys)



### November 13, 2023 - 20.0.0

```
tines/tines-app: sha256:a86a1ed0bbbe884af730c39e2d6546c963b1a837b5ace50f32b82a01e3faf013
tines/tines-nginx: sha256:72c11d39493d689e51c6fe63d7fd62d6131bdc5d56fb31d6de6a215536b13e93
```

- [Credentials and resources added to the `INFO` keyword](https://www.tines.com/whats-new/credentials-and-resources-added-to-the-info-keyword)
- [Linked cases and case buttons API](https://www.tines.com/whats-new/linked-cases-and-case-buttons-api)
- [`DISTANCE_OF_TIME_IN_WORDS` function](https://www.tines.com/whats-new/distance-of-time-in-words-function)
- [`DATE_DIFF` function](https://www.tines.com/whats-new/date-diff)
- [In-product notifications for change control](https://www.tines.com/whats-new/in-product-notifications-for-change-control)
- [Page charts](https://www.tines.com/whats-new/page-charts)

### November 13, 2023 - 19.3.2

```
tines/tines-app: sha256:406e5634045ff332d86b209120fd3e9bf58138aa3e3186715ff3af4186966261
tines/tines-nginx: sha256:72c11d39493d689e51c6fe63d7fd62d6131bdc5d56fb31d6de6a215536b13e93
```

- Fixes a schema migration issue that was preventing some users from upgrading

### ~~November 10, 2023 - 19.3.1~~ [Deprecated in favor of  19.3.2]

```
tines/tines-app: sha256:9b6e766958400de9103ca75e003f5d5fc7d78307a8af245c3f2542215acc3680
tines/tines-nginx: sha256:069837b0091424dd35dfb1d7ab3e2bf826c6d2ce2ed88e71eaadb56a97fd3a7e
```

- Fixes a schema migration issue that was preventing some users from upgrading

### October 31, 2023 - 18.3.5

```
tines/tines-app: sha256:56d73d02f0e46af9ed4f2a4da9a6f5a4547e36a7ddd571f43ba4265dd67dfb53
tines/tines-nginx: sha256:2636f5073b173e2ba55cef19772c393030db33d1810ab859bbf52eda5eb9240e
```

- Fixes deprecation warning on page Map element

### ~~October 31, 2023 - 18.3.4~~ [Deprecated in favor of  18.3.5]

```
tines/tines-app: sha256:905a97357ba136375aa320f8253a5d903feb9ee1241ad092c0c8ada2420fbeee
tines/tines-nginx: sha256:b359c59739d83e4ced41bd06e4b893b32fd44aab8b07a7f607fb10967bd45c83
```

> **WARNING:** This patch reverts a Debian upgrade to ensure backward compatibility with Docker versions below 20.10.10. This also reverts certain security upgrades and may bring back CVEs that were resolved in v18.3. Upgrading to v19 will resolve those.

- Revert Debian upgrade introduced in 18.3.0, in order to maintain compatibility with older Docker versions. This includes changing the base image of `tines-ruby` back to Debian Bullseye.

### ~~October 30, 2023 - 19.3.0~~  [Deprecated in favor of  19.3.2]

```
tines/tines-app: sha256:bb6084b854db4c5ccd58bd1e049ecb08c8040e98286ad80093009fc7d78b8a8b
tines/tines-nginx: sha256:c01733169d6b09f9ab1ac2009b4b408fc97a85fa67033d6cc59dca3f5362b8aa
```

- [`ERROR` function](https://www.tines.com/whats-new#error-function)
- [Full screen pages](https://www.tines.com/whats-new#full-screen-pages)
- [Autofill Implode paths from the latest Explode action options](https://www.tines.com/whats-new/autofill-implode-paths-from-the-latest-explode-action-options)
- [Terraform provider](https://www.tines.com/whats-new/terraform-integration)
- [Page collections](https://www.tines.com/whats-new/page-collections)
- [New case manager role](https://www.tines.com/whats-new/new-case-manager-role)
- [Custom roles](https://www.tines.com/whats-new/custom-roles)
- [More charts for records reports](https://www.tines.com/whats-new/more-charts-for-records-reports)
- [Change control via API](https://www.tines.com/whats-new/change-control-via-api)

### October 28, 2023 - 17.3.8

```
tines/tines-app: sha256:0d7e0303ce4a5b283b847dba55684fbe604dcbc51655fd7e5f8ebf55663dd4ad
tines/tines-nginx: sha256:755b1e9141441843cfe53952da9f48e571564122697ab5004c1df0da3b9f0c95
```

- Fixes deprecation warning on page Map element

### October 25, 2023 - 19.2.1

```
tines/tines-app: sha256:1daab169896a7137c065e7c4d6f72ceeb6c87f1ab21b0fcca948647618e47a76
tines/tines-nginx: sha256:50a69cd7441fdafed4e564fd5445f14f9d7ab0a6e6cb5c8c7db468a0f03277e6
```

- Bug fixes for configuring licenses

### ~~October 16, 2023 - 19.2.0~~ [Deprecated in favor of  19.2.1]

```
tines/tines-app: sha256:9d11032d490ce599bf08ceabf6aa43c43c7fa9e394836c27c8c5183fcddc35b9
tines/tines-nginx: sha256:97b692963a6ea0acfb6f6974c6911bee1f50be363e38c8fd7f525c1361375d82
```

- [Hello, button groups!](https://www.tines.com/whats-new/hello-button-groups)
- [New options for interacting with the records API](https://www.tines.com/whats-new/new-options-for-interacting-with-the-records-api)
- [Trash is now archived stories](https://www.tines.com/whats-new/trash-moved-and-is-now-archive)
- Performance improvement for Deduplicate actions

### October 2, 2023 - 19.1.0

```
tines/tines-app: sha256:6a949ae427ebbd5db101ba2e8e70344488e449cb84e441c8f82e64f061d6fa11
tines/tines-nginx: sha256:3b3c0bf56ec537cea194135a3fe71eee61b8f193dd58213e5ebe2b07d9b123dd
```

- [Tenant-wide settings for change control](https://www.tines.com/whats-new/tenant-wide-settings-for-change-control)
- [Jump from event to story run](https://www.tines.com/whats-new/jump-from-event-to-story-run)
- [Change control authorship](https://www.tines.com/whats-new/change-control-authorship)
- [Upload custom images to your icon picker](https://www.tines.com/whats-new/upload-custom-images-to-icon-picker)
- [Combined events for live and test environments](https://www.tines.com/whats-new/combined-events-for-live-and-test-environments)
- [Expanded visualizations for records](https://www.tines.com/whats-new/expanded-visualizations-for-records)
- [Saved views for records](https://www.tines.com/whats-new/saved-views-for-records)
- [Manage cases metadata via new API endpoints](https://www.tines.com/whats-new/manage-cases-metadata-via-new-api-endpoints)
- [Improved tag readability](https://www.tines.com/whats-new/improved-tag-readability)
- Security fixes for WebP CVE

### October 2, 2023 - 19.0.3

```
tines/tines-app: sha256:334a003506a9a22f85ff83a6410a218d0fa2fc9717d0c84bf4a5656115083b1d
tines/tines-nginx: sha256:be84b0c716d704cbf76514ec1acd1d71c4de168f3a91705dbacccf583f01e142
```

- Bug fixes for new Docker Compose setup process
- **Note: From version 19.0 onwards, Tines requires PostgreSQL 14, Redis 6.2 or above and Docker version 20.10.10 or above.**

### ~~October 2, 2023 - 18.3.3 ~~[Deprecated in favor of  18.3.5]

```
tines/tines-app: sha256:608d57815c240f242eb80f46558ad2e1b3335cbe4ad5b74d517cc326b738115f
tines/tines-nginx: sha256:41156ca518a7f287b856af2ae7d0006158dd9cecb8d2f7368fe3f6ee6b123c4d
```

- Security fixes for WebP CVE

### ~~September 29, 2023 - 19.0.2~~ [Deprecated in favor of  19.0.3]

```
tines/tines-app: sha256:01276dfafdfacb3e1b4e50316879396dfc46e61aecdd4e49ddd912f214bb20c5
tines/tines-nginx: sha256:5e4274ef509b82bfdf098fbca1e7953171cddb356e031660dbc85d9ac2c24789
```

- Security fixes for WebP CVE
- **Note: From version 19.0 onwards, Tines requires PostgreSQL 14, Redis 6.2 or above and Docker version 20.10.10 or above.**

### ~~September 19, 2023 - 19.0.0~~ [Deprecated in favor of  19.0.3]

```
tines/tines-app: sha256:bf6646db384b7f4c063627d70b35fa60ed3fc3943d87da6821c485ca2837afdc
tines/tines-nginx: sha256:07d37fcabaa35a1fc68f7d7940b4dea70b4e340e40b2f86c5979802bd74f77a3
```

- [Team level access control for pages](https://www.tines.com/whats-new/team-level-access-control-for-pages)
- [Getting back to your story from viewing a page](https://www.tines.com/whats-new/getting-back-to-your-story-from-viewing-a-page)
- [Tenant-wide settings for change control](https://www.tines.com/whats-new/tenant-wide-settings-for-change-control)
- Bug fixes
- **Note: From version 19.0 onwards, Tines requires PostgreSQL 14, Redis 6.2 or above and Docker version 20.10.10 or above.**

### ~~September 5, 2023 - 18.3.1~~ [Deprecated in favor of  18.3.5]

```
tines/tines-app: sha256:421ad6b670eb27384b5f4bb3a9e2cc4efea53332fe4bf36df28abb60fe34ecba
tines/tines-nginx: sha256:07d37fcabaa35a1fc68f7d7940b4dea70b4e340e40b2f86c5979802bd74f77a3
```

- [Custom URLs for pages](https://www.tines.com/whats-new/custom-urls-for-pages)
- [`DATE` function improvements](https://www.tines.com/whats-new/date-function)
- [Subscription options for case alerts](https://www.tines.com/whats-new/subscription-options-for-case-alerts)
- [Edit name and description for API keys](https://www.tines.com/whats-new/edit-name-and-description-for-api-keys)
- [Introducing approval flows for change control](https://www.tines.com/whats-new/introducing-change-control-approval-flows)
- [Improved Markdown code blocks](https://www.tines.com/whats-new/improved-markdown-code-blocks)
- [Markdown: hyperlink over text with copy and paste](https://www.tines.com/whats-new/markdown-hyperlink-over-text-with-copy-paste)
- [Clear logs for multiple actions](https://www.tines.com/whats-new/clear-logs-for-multiple-actions)
- [Improved storyboard navigation](https://www.tines.com/whats-new/improved-storyboard-navigation)
- [Drag and drop stories into folders](https://www.tines.com/whats-new/drag-and-drop-stories-into-folders)
- [`DATE_PARSE` function](https://www.tines.com/whats-new/date-parse-function)

### August 21, 2023 - 18.2.0

```
tines/tines-app: sha256:cfd85a4589920a73286877e0ef0d4897a224ced6ee0ad801697387235fab1ecc
tines/tines-nginx: sha256:1434ef27a24ef7f8e4f05c396fec54bcc92f3db1feb9455f67a0776fbd26ddb
```

- [New description field for API keys](https://www.tines.com/whats-new/new-description-field-for-api-keys)
- [Rename a story on import](https://www.tines.com/whats-new/rename-a-story-on-import)
- [Take quick action on your cases list](https://www.tines.com/whats-new/take-quick-action-on-your-cases-list)

### August 16, 2023 - 18.0.2

```
tines/tines-app: sha256:d37a082f7cdea478e45479c1cae9a767e225b02a38d9c7dd17c0516af070068b
tines/tines-nginx: sha256:a99726e8c776a7032ee9ce59237bb1a4374d65921990fc28e7fc760fadb683ea
```

#### Changes

- Fixed issue with a database schema change

### August 15, 2023 - 18.1.5

```
tines/tines-app: sha256:a7e255b896574b0f2c6f43f884922e5c5b4aef1bb58f6fb7907ffd4dde9d49e6
tines/tines-nginx: sha256:fc8a633156e24c63404e09bc6fd9f617cd22979c73fff59fbd3d845c5c3f9fa3
```

#### Changes

- Fixed issue with a database schema change

### ~~August 14, 2023 - 18.1.4~~ [Deprecated in favor of  18.1.5]

```
tines/tines-app: sha256:6e04d8d86de661ee3b89c40aeea684843d2cb09c441a86d0427c082a0dcb9375
tines/tines-nginx: sha256:9901374760c4946d462a80432ace68ef559423fd5ac3cbbe01381fd1fa8c0ac6
```

#### Changes

- security fix for webhook api results

### ~~August 11, 2023 - 18.1.3~~ [Deprecated in favor of  18.1.5]

```
tines/tines-app: sha256:2302a9d4e0c261f9119162b2b0a38ef867e8720a6a4ca886d4f48140ec26646d
tines/tines-nginx: sha256:2b29ab8b82fe05bbe5dc38f7a242845334ab2acac3dbef8541a34105ff60e673
```

#### Changes

- [Self-hosted troubleshooting scripts](https://www.tines.com/docs/self-hosting/docker-compose/troubleshooting-tines-on-docker-compose#troubleshooting-scripts)

### August 11, 2023 - 17.3.7

```
tines/tines-app: sha256:adf779e418a6c86b8b40b0c6960398ab5d94cbe7bf9b9486faae3049cf9e0b7f
tines/tines-nginx: sha256:476599a167eeb06cad1ccf3edfb2d72020d5163d2f9f61a5e99c646d9c4d97c5
```

#### Changes

- [Self-hosted troubleshooting scripts](https://www.tines.com/docs/self-hosting/docker-compose/troubleshooting-tines-on-docker-compose#troubleshooting-scripts)

### ~~August 7, 2023 - 18.1.0~~ [Deprecated in favor of  18.1.5]

Image digests:

```
tines/tines-app: sha256:985092a44747a4fa6b08bc1d6e1c8343570bfb9dd0e8e866fac3de8640e5487b
tines/tines-nginx: sha256:666fed45f7396cd338eb860badf47fbac0ef3eed0bf18ce9fe42610f4e7d601b
```

- [Chart time series data in records](https://www.tines.com/whats-new/chart-time-series-data-in-records)
- [Attach downstream actions to cases](https://www.tines.com/whats-new/attach-downstream-actions-to-cases)
- [Tags are now included in the stories API](https://www.tines.com/whats-new/tags-are-now-included-in-the-stories-api)
- [Multiple buttons on a page](https://www.tines.com/whats-new/multiple-buttons-on-a-page)

### ~~August 1, 2023 - 18.0.1~~ [Deprecated in favor of  18.0.2]

```
tines/tines-app: sha256:ec8d1f730549a06acdcdfa0bc65df0adaffdad4091b10c24f0477a40240ee20c
tines/tines-nginx: sha256:bf11fe593a60ea88166236c8786877a4365eeb519698655a4f69614b25a1e76b
```

#### Changes

- Fix for Records list API pagination.

### ~~August 1, 2023 - 17.3.4~~ [Deprecated in favor of  17.3.7]

```
tines/tines-app: sha256:b9a5d566f4899141f9fe063c41d7ea7b079e17f650360b17a98584f26e925147
tines/tines-nginx: sha256:bf11fe593a60ea88166236c8786877a4365eeb519698655a4f69614b25a1e76b
```

#### Changes

- Fix for Records list API pagination.

### ~~**Jul 24, 2023 - 18.0.0**~~** **[Deprecated in favor of  18.0.2]

Image digests:

```
tines/tines-app: sha256:1902f095694da80c8bab13f359b2dcebcfe67113e51d80bbc49b4d177f3193f8
tines/tines-nginx: sha256:bf11fe593a60ea88166236c8786877a4365eeb519698655a4f69614b25a1e76b
```

- [Quickly copy event path formula](https://www.tines.com/whats-new#quickly-copy-event-path-formula)
- [Cases improvements roundup](https://www.tines.com/whats-new#cases-improvements-roundup)
- [Records API: GET and DELETE endpoints](https://www.tines.com/whats-new#records-api-get-and-delete-endpoints)
- [Diffs for change control & versions](https://www.tines.com/whats-new#diffs-for-change-control-versions)
- [Trigonometric functions](https://www.tines.com/whats-new#trigonometric-functions)
- [Visual indicator for scheduled actions](https://www.tines.com/whats-new#visual-indicator-for-scheduled-actions)
- [Markdown support in page table cells](https://www.tines.com/whats-new#markdown-support-in-page-table-cells)
- [Drag or paste attachments to cases](https://www.tines.com/whats-new#drag-or-paste-attachments-to-cases)
- [Read and write metadata in cases API](https://www.tines.com/whats-new#read-and-write-metadata-in-cases-api)
- [Execute Python scripts with dependencies](https://www.tines.com/whats-new#execute-python-scripts-with-dependencies)
- Bug Fixes

### ~~**Jul 19, 2023 - 17.3.3**~~** **[Deprecated in favor of  17.3.7]

Image digests:

```
tines/tines-app: sha256:a381ea9f57de1e39dff6ed6bbb0a0819d8434134955665bbbe641721a3a6aa48
tines/tines-nginx: sha256:bf11fe593a60ea88166236c8786877a4365eeb519698655a4f69614b25a1e76b
```

- Bug fix for provisioning customer certificate from admin tool
- Ability to disable IPV6 by setting `DISABLE_NGINX_IPV6=true` in `.env` 
- Ability to setup `tines` user on host machine. Can be performed for new installations by setting `SETUP_DEDICATED_LINUX_USER=true` in `.env`

### ~~**Jul 14, 2023 - 17.3.2**~~** **[Deprecated in favor of  17.3.7]

Image digests:

```
tines/tines-app: sha256:6b50d597ce2d4178fa6e3578ac8097d576b0842b12fb381836ce2722c045bc90
tines/tines-nginx: sha256:5de703b100d8c4644c8e7f3435901d5bba12509e8d44c5d6702c657603bfe79d
```

Changes

- Bug fix - capturing records on the storyboard

### ~~**Jul 13, 2023 - 17.3.1**~~** **[Deprecated in favor of  17.3.7]

Image digests:

```
tines/tines-app: sha256:7c2da262ec4df0016e6e0716454cf6f083104a83bd31aa71d6f5a559d2212c8c
tines/tines-nginx: sha256:5de703b100d8c4644c8e7f3435901d5bba12509e8d44c5d6702c657603bfe79d
```

Changes

- Bug fix - sending requests to toolkit.tines.com

### ~~**Jul 10, 2023 - 17.3.0**~~** **[Deprecated in favor of  17.3.7]

Image digests:

```
tines/tines-app: sha256:699887f05efa98421cf164aeb889c6268beea1f1e3214f732e82fcf1a85f977b
tines/tines-nginx: sha256:5de703b100d8c4644c8e7f3435901d5bba12509e8d44c5d6702c657603bfe79d
```

Changes

- [Option to clear all action logs and memory from a story](https://www.tines.com/whats-new/clear-all-option-for-action-and-story-logs)
- [Story time saved](https://www.tines.com/whats-new/story-time-saved) 
- [HTML editor for email action](https://www.tines.com/whats-new/html-editor-for-email-action)
- [Introducing touch support for Tines](https://www.tines.com/whats-new/introducing-touch-support-for-tines)
- [Linking cases](https://www.tines.com/whats-new/linking-cases)
- [Records API](https://www.tines.com/whats-new/records-api)
- [Icons and emojis for your stories, credentials, and resources 📗 🎉 ✅ 😁](https://www.tines.com/whats-new/icons-and-emojis-for-your-stories-credentials-and-resources)

### **Jun 29, 2023 - 17.2.1**

Image digests:

```
tines/tines-app: sha256:e4b0fbe30598f7e2caa674977bd4d3c37d3cfb9adfc7709f908807dc4e6c185e
tines/tines-nginx: sha256:5de703b100d8c4644c8e7f3435901d5bba12509e8d44c5d6702c657603bfe79d
```

Changes

- Security update
- Bug fix - accessing cases API

### ~~**Jun 26, 2023 - 17.2.0**~~** **[Deprecated in favor of  17.2.1]

Image digests:

```
tines/tines-app: sha256:c28f2997fc72ddcb98bd0da000386f6bb11ec7624b43570dced52db1173ae37a
tines/tines-nginx: sha256:5de703b100d8c4644c8e7f3435901d5bba12509e8d44c5d6702c657603bfe79d
```

Changes 

- [Cases and Records release for self-hosted environment](https://www.tines.com/whats-new/introducing-cases-and-records)

### **Jun 26, 2023 - 17.1.0**

Image digests:

```
tines/tines-app: sha256:49ea852c5026b0eb44f0bd3c07c0661e63e3a06fa2370ae2fb695ba93f913809
tines/tines-nginx: sha256:5de703b100d8c4644c8e7f3435901d5bba12509e8d44c5d6702c657603bfe79d
```

Changes 

- [Redacted credentials in HTTP request action logs for live environments](https://www.tines.com/whats-new/log-requests-with-redacted-credentials-for-all-actions)
- [Mark a story as favourite](https://www.tines.com/whats-new/mark-a-story-as-favorite)
- Bug fixes

### **Jun 20, 2023 - 17.0.3**

Image digests:

```
tines/tines-app: sha256:d77633c1c8f6ff213950670ebb02cd393f675d10574d266795e77cc8af2315c2
tines/tines-nginx: sha256:8ac1fbeb7a01c8a9a5c06d23d8a78e9bafc7a3a0383b795c77f6b747a761d593
```

Changes 

- Bug fix for startup command

### ~~**Jun 17, 2023 - 17.0.2**~~** **[Deprecated in favor of  17.0.3]

- Image digests:

```
tines/tines-app: sha256:6b25a3f2a3999e306cc497c90fef2903384e10009a19f5565a03685ab814c8b9
tines/tines-nginx: sha256:e05abbd84e3c7ff93524cf12fbab3273a5299edc62ddf72e8121e5e813aaaac5
```

- Changes 
  
  - Integration sync bug fix

### ~~**Jun 12, 2023 - 17.0.1**~~** **[Deprecated in favor of  17.0.3]

Image digests:

```
tines/tines-app: sha256:a17f5b7b45d342e62185f9cf4fb26a004c3cb3c7d5a38004d33dde6bd67976f5
tines/tines-nginx: sha256:e5ad46cc9e226626d03fa93d6200465d5fe2a12c1a2c782437bc57f41946d42b
```

Changes 

- Security fixes

### ~~**Jun 12, 2023 - 17.0.0**~~** **[Deprecated in favor of  17.0.3]

Image digests:

```
tines/tines-app: sha256:2f42000795ec906006ed4cf0e2f53eaa4566b7ef32a8d160dd884805e91d7691
tines/tines-nginx: sha256:f83dfece194403068170ff81b7707b19bd6248d269336a8cfda0c2fabb7c9643
```

Changes 

- [7z support](https://www.tines.com/whats-new/unzip-function-now-supports-7z-archives)
- [Rotate function](https://www.tines.com/whats-new/rotate-function)
- [Additional parameters for group actions](https://www.tines.com/whats-new/additional-parameters-for-group-actions)
- [Log HTTP requests with redacted credentials](https://www.tines.com/whats-new/log-http-requests-with-redacted-credentials)
- [Automated credentials for Tines API keys](https://www.tines.com/whats-new/automated-credentials-for-tines-api-keys)
- Security fixes
- Bug fixes

### **Jun 17, 2023 - 16.3.2**

Image digests:

```
tines/tines-app: sha256:77096e7c8198825e7a0916dca0b3d73b969ed0863699785d98b86f7eb35c705c
tines/tines-nginx: sha256:69e3bb555353d3b876102239e448455c049e2c29c631f9e3c41bbaa7ac2cb539
```

Changes 

- Integration sync bug fix

### **June 9, 2023 - 16.3.1**

**Image digests:**

```
tines/tines-app: sha256:ff8880257c41f768442cde92aed0a303c2d0235e97acf581f8926ea12b738dcb
tines/tines-nginx: sha256:f83dfece194403068170ff81b7707b19bd6248d269336a8cfda0c2fabb7c9643
```

Changes:

- Bug fix for startup command

### **May 29, 2023 - 16.3.0**

**Image digests:**

```
tines/tines-app: sha256:ebe38a716e2e539718c1b8b5c0479686815a6d24e7ea62e2723990d3144c660f
tines/tines-nginx: sha256:f83dfece194403068170ff81b7707b19bd6248d269336a8cfda0c2fabb7c9643
```

Changes:

- [Higher TTL on HTTP credentials](https://www.tines.com/whats-new/higher-ttl-on-http-credentials) 
- [New story API settings pane  ](https://www.tines.com/whats-new/new-story-api-settings-pane)
- [Tolerance for deduplicating events](https://www.tines.com/whats-new/tolerance-for-deduplicating-events)
- [FILTER function fully mirrors REJECT](https://www.tines.com/whats-new/filter-function-fully-mirrors-reject)
- [RANDOM_STRING function](https://www.tines.com/whats-new/random-string-function)
- [New array functions: PUSH, INSERT, and DELETE](https://www.tines.com/whats-new/push-function)
- [NUMBER function works with negative numbers](https://www.tines.com/whats-new/number-function-works-with-negative-numbers)
- [SET_KEY function](https://www.tines.com/whats-new/wip-set-key-function)
- [Action running indicators](https://www.tines.com/whats-new/action-running-indicators)
- Bug fixes

### **May 24, 2023 - 16.2.3**

**Image digests:**

```
tines/tines-app: sha256:336184a0ab7038f980ee9ac443cad77fad3556c68c146840d725ae210968ec54
tines/tines-nginx: sha256:f83dfece194403068170ff81b7707b19bd6248d269336a8cfda0c2fabb7c9643
```

Changes:

- Support for custom container names in Postgres upgrade script

### **May 24, 2023 - 15.3.4**

**Image digests:**

```
tines/tines-app: sha256:7e4bce793c26a9feae0db394ce1d247850ee711b122fee1b59569445b5a6c3de
tines/tines-nginx: sha256:f2b4bfe27ef7331ef863dc321836d546e96f1c82b4d7e52ddc3a0a1e1bfce54b
```

Changes:

- Support for custom container names in Postgres upgrade script

### **May 16, 2023 - 16.2.1**

**Image digests:**

```
tines/tines-app: sha256:5ef3519e106bc24f63cae20253a7ea2fe6c6cd53c14cea46469e9977c776224b
tines/tines-nginx: sha256:f83dfece194403068170ff81b7707b19bd6248d269336a8cfda0c2fabb7c9643
```

Changes:

- Database Migration Bug fix



### **May 15, 2023 - 16.2.0**

> **NOTE:** We've updated our supported Nginx version to 1.24

**Image digests:**

```
tines/tines-app: sha256:fe0f74af4b9529d44657cf09c9c07827de9909492d11e70f25e773acb9c879f9
tines/tines-nginx: sha256:f83dfece194403068170ff81b7707b19bd6248d269336a8cfda0c2fabb7c9643
```

Changes:

- Add CrowdStrike to list of known OAuth providers
- [`no match` branch for trigger actions](https://www.tines.com/whats-new/no-match-secondary-links-for-trigger-actions)
- [Add DIFFERENCE, INTERSECTION, and UNION formula functions](https://www.tines.com/whats-new/difference-intersection-union-functions)
- Edit Tags
- Dockerfile updated
- Bug fixes

### **
May 12, 2023 - 16.1.1**

Image digests:

```
tines/tines-app: sha256:dcd62d87fc3f619a1f8f04348580f8921eab2319334f4562c652d5ad66050b1f
tines/tines-nginx: sha256:3173f5f85aabdede6297df624c4990a16c392faf550edae5229ae125fc31070d
```

Changes

- Bug fixes
- Support Postgres upgrades for multiple tenants on a single system

### **May 12, 2023 - 15.3.3**

Image digests:

```
tines/tines-app: sha256:0fda68c1ed070fc65ea5cfe2561b922ccefd3f8c9f508910862a1b260f8e51ee
tines/tines-nginx: sha256:3173f5f85aabdede6297df624c4990a16c392faf550edae5229ae125fc31070d
```

Changes

- Bug fixes
- Support Postgres upgrades for multiple tenants on a single system

### **May 3, 2023 - 15.3.2**

Image digests:

```
tines/tines-app: sha256:42efd0df70197f2aa3badc7d0e840249b4c576bc3cbad62b6391d5afc73aec88
tines/tines-nginx: sha256:3173f5f85aabdede6297df624c4990a16c392faf550edae5229ae125fc31070d
```

Changes

- Support for Postgres BINARY upgrades from PG11 to PG14 in `upgrade-postgresql11.sh` 

### **May 2, 2023 - 16.1.0**

Image digests:

```
tines/tines-app: sha256:e3111e46ad4ace7721392db220085b83669e8cb29ace84e60384fa994b0a97b6
tines/tines-nginx: sha256:3173f5f85aabdede6297df624c4990a16c392faf550edae5229ae125fc31070d
```

Changes 

- [Auto submitting pages](https://www.tines.com/whats-new/auto-submitting-pages)
- [Quickly select multiple events](https://www.tines.com/whats-new/quickly-select-multiple-events)
- [RSA functions for formulas](https://www.tines.com/whats-new/introducing-rsa-formulas-functions)
- [Content negotiation in webhook responses](https://www.tines.com/whats-new/content-negotiation-in-webhook-response)
- The `tines-app` Docker image can now be pulled from Docker Hub using [Docker Content Trust](https://docs.docker.com/engine/security/trust/) for additional security:
  
  - `DOCKER_CONTENT_TRUST=1 docker pull tines/tines-app`

### **April 17, 2023 - 16.0.0**

Image digests:

```
tines/tines-app: sha256:63e3ffea0e28d146f6d46b1262f0152ff1b5ce28ce80cd24760418c7145c3e3b
tines/tines-nginx: sha256:3173f5f85aabdede6297df624c4990a16c392faf550edae5229ae125fc31070d
```

Changes 

- [Story tags](https://www.tines.com/whats-new/story-tags)
- [New look for groups and action templates](https://www.tines.com/whats-new/new-look-for-templates)
- [Team story allocation](https://www.tines.com/whats-new/team-story-limits)
- [Anonymize page submissions](https://www.tines.com/whats-new/anonymize-page-submissions)
- [Rich text in pages with Markdown](https://www.tines.com/whats-new/rich-text-in-pages-with-markdown)
- Security fixes
- Dockerfile updated

### **April 10, 2023 - 15.3.1**

Image digests:

```
tines/tines-app: sha256:0e64fbab41d42b4d69837ae640cf87d4b20d8d02c6660937168c1b55f5d40b4c
tines/tines-nginx: sha256:08bd3c017a58856908d49b73208b69db3528358119c2ef3097f268e53ad48cdd
```

Changes

- Minor security fixes

### **April 3, 2023 - 15.3.0**

Image digests:

```
tines/tines-app: sha256:25541ec1d21d43d94221e22446b2df29a566db93e2ed70fa80d22d2dcaf3057e
tines/tines-nginx: sha256:08bd3c017a58856908d49b73208b69db3528358119c2ef3097f268e53ad48cddff8b7c37
```

Changes

- [Command bar improvements](https://www.tines.com/whats-new/command-bar-improvements)
- [Search events for text](https://www.tines.com/whats-new/search-action-events-using-a-substring)

### **March 28, 2023 - 15.2.2**

Image digests:

```
tines/tines-app: sha256:96f9c6719060d55497ac410f27dd28b54562511a05b16d2fa5dfd7359ecd303a
tines/tines-nginx: sha256:af0a329d6f5ebffa4576d5384169731b8e0f2e73e706d667108125daff8b7c37
```

Changes

- Fix issue preventing story publishing via the API

### **March 24, 2023 - 15.2.1**

Image digests:

```
tines/tines-app: sha256:10ac78971e310f4e7fcb1745d50e14e47524b312a78f710dad61137e75e53267
tines/tines-nginx: sha256:5d19422bdc111c6daaec1d552e0b72be38d2253c867f10e1669614de32c34681
```

Changes

- OpenSSL security fixes

### **March 20, 2023 - 15.2.0**

Image digests:

```
tines/tines-app: sha256:bdc211d2231635f34990124e01dad694c7bd8bf79f5119211dbe0be13cd8bb39
tines/tines-nginx: sha256:5d19422bdc111c6daaec1d552e0b72be38d2253c867f10e1669614de32c34681
```

Changes 

- [`PARQUET_PARSE` function](https://www.tines.com/whats-new/parquet-parse-function)
- [HTTP Credential TTL](https://www.tines.com/whats-new/http-credential-ttl)
- [`HEX_PARSE` and `TO_HEX` functions](https://www.tines.com/whats-new/hex-parse-and-to-hex-functions)
- [Emit event on HTTP request action retry failures](https://www.tines.com/whats-new/better-handling-of-retry-failures)
- [`YAML_PARSE`, `TO_YAML` and `TO_XML` functions](https://www.tines.com/whats-new/yaml-parse-to-yaml-and-to-xml-functions)
- [Respond to web requests in Send to Story](https://www.tines.com/whats-new/event-data-in-webhook-responses)
- [Double click Send to Story action to open](https://www.tines.com/whats-new/double-click-send-to-story-action-to-open)
- Updated Docker build configuration

### **March 16, 2023 - 15.1.4**

Image digests:

```
tines/tines-app: sha256:f38897aea9a88abc8a6aa1de658f57192e47529536d1c687c43caa1b9fb093e0
tines/tines-nginx: sha256:5d19422bdc111c6daaec1d552e0b72be38d2253c867f10e1669614de32c34681

```

Changes 

- Upgrade to most recent version of Cloudflare Tunnel client

### **March 16, 2023 - 14.3.3**

Image digests:

```
tines/tines-app: sha256:c4f11efb96a6b91b543a898cbab309c2ade0c69eb3cf63569b64a25b55c4ebc7
tines/tines-nginx: sha256:0e22958969d47e44f8c31efd5c49bf4d6797e31fc7048debbf332e105bd053d3
```

Changes 

- Upgrade to most recent version of Cloudflare Tunnel client

### **March 16, 2023 - 15.1.3**

Image digests:

```
tines/tines-app: sha256:fda21ac57e9dba425f2a1e2228e9b6887b781e1b13babecb02091079415cc2e6
tines/tines-nginx: sha256:5d19422bdc111c6daaec1d552e0b72be38d2253c867f10e1669614de32c34681

```

Changes 

- Bug fix for fetching SSO configuration when environment variable is missing

### **March 10, 2023 - 14.3.2**

Image digests:

```
tines/tines-app: sha256:dc1486235f8201992955466b1903a570d69e58013dee0c6518436212614afb78
tines/tines-nginx: sha256:6f18cca39f6805ede7319a05ed5c69c87b42aaf8b880bc208cad146b3917c4f2
```

Changes 

- Make [Redis Sentinel](https://www.tines.com/docs/self-hosting/using-redis-sentinel) available on 14.x

### **March 8, 2023 - 15.1.2**

Image digests:

```
tines/tines-app: sha256:55e8ad9d03d832a5b22aa0670f5ea980db861867a1d8d70fe6342d6982416c78
tines/tines-nginx: sha256:999d6a9e2d3c3b5b0fb7c2e79070a747bef99d9e2b024263fc299f5afe426166
```

Changes 

- Bug fixes in `upgrade-postgresql11.sh` to better support stacks running in air-gapped environments.

### **March 7, 2023 - 15.1.1**

Image digests:

```
tines/tines-app: sha256:743d9f904ed171622f308c22397e38d837cf8aba2aa1c3becd829e1efa0a03fd
tines/tines-nginx: sha256:999d6a9e2d3c3b5b0fb7c2e79070a747bef99d9e2b024263fc299f5afe426166
```

Changes 

- Bug fixes to `upgrade.sh` to safely determine the running postgres version
- Bug fixes to `upgrade-postgresql11.sh` for better `docker-compose` setup

### **March 6, 2023 - 15.1.0**

Image digests:

```
tines/tines-app: sha256:043d67ef1d8e7fd51dd0c773544ca00dba884dadf0d4689874090e6d9774b624
tines/tines-nginx: sha256:999d6a9e2d3c3b5b0fb7c2e79070a747bef99d9e2b024263fc299f5afe426166
```

Changes 

- [GZIP and GUNZIP functions](https://www.tines.com/whats-new/gzip-and-gunzip-functions)
- [Just-in-time user provisioning](https://www.tines.com/whats-new/just-in-time-user-provisioning)
- [Logical Operators in formulas](https://www.tines.com/whats-new/logical-operators-in-formulas)
- [Formulas in keys](https://www.tines.com/whats-new/formulas-in-keys)
- [Custom field validation](https://www.tines.com/whats-new/custom-field-validation)
- [Array and Object literals](https://www.tines.com/whats-new/array-and-object-literals)
- [Draggable elements in the pages editor](https://www.tines.com/whats-new/draggable-elements-in-the-page-editor)
- [Reusable themes for pages](https://www.tines.com/whats-new/reusable-themes-for-pages)
- [Command bar](https://www.tines.com/whats-new/command-bar)
- Security fixes
- Documentation live for [Upgrading from PostgreSQL 11.x to PostgreSQL 14.5](https://www.tines.com/docs/self-hosting/upgrading-from-postgresql-11-x-to-postgresql-14-5)

### **February 20, 2023 - 15.0.0**

Image digests:

```
tines/tines-app: sha256:dd9660cb9e2740e611683478b8931ebf0a9931f3f664f1d9039c2a2967c162a9
tines/tines-nginx: sha256:999d6a9e2d3c3b5b0fb7c2e79070a747bef99d9e2b024263fc299f5afe426166
```

Changes 

- [Page width configuration](https://www.tines.com/whats-new/page-width-configuration)
- [Page loading message](https://www.tines.com/whats-new/page-loading-message)
- PostgreSQL 14 compatibility

### **February 14, 2023 - 14.3.1**

Image digests:

```
tines/tines-app: sha256:81fd7f8aea9b5abec659a960b9c072929d83926ae7c0a3f6ea11757c05b83120
tines/tines-nginx: sha256:43933c9e02e6e28e8fefd91e2cf9292fbf120d6b6aefa8106b0bdafad6c194c1
```

Changes 

- Bug fix to ensure running SMTP without TLS works succesfully

### **February 7, 2023 - 14.3.0**

Image digests:

```
tines/tines-app: sha256:e35199ddd71db91a1a7c8eaeef017fa3afb74d1990ccee64648cc9b2703035b7
tines/tines-nginx: sha256:cbc68f16be1c664010bf0ce1932a1d8a26512171f8ef29a582eca97f868379d5
```

Changes 

- [Story Level Monitoring](https://www.tines.com/whats-new/story-level-monitoring)
- [Export story as image](https://www.tines.com/whats-new/export-story-as-image) 

### **February 17, 2023 - 14.2.4**

Image digests:

```
tines/tines-app: sha256:8790d438d8fcf393a418c3bf9aa39649e5c5b30f1815e89ba7f6b94465d91e78
tines/tines-nginx: sha256:629e917f3b3184b5ca898e404044dac4021b06415da0639e3c33dd397259cf05
```

Changes 

- Support running nginx with `tines-app` web service with SSL

### **February 6, 2023 - 13.3.8**

Image digests:

```
tines/tines-app: sha256:f5bfa565cca74661698a8ed1b004291879d2896138754c176f84880bda563d56
tines/tines-nginx: sha256:d68735ea39c9066fa69ab2c3b884fc9b03c12c0a0d5c51a3291438471a74ee87
```

**Changes**

- Bug fixes for Redis Sentinel support (Experimental)

### **February 3, 2023 - 13.3.7**

Image digests:

```
tines/tines-app: sha256:2a609668b7f319605216bf089e617f46059e5c2e339f40ccdb235bb4a6404522
tines/tines-nginx: sha256:dadb44af13267497e4ba04c7239c49f9c389e559ad540abe970402357b7eada5
```

**Changes**

- Bug fixes for Redis Sentinel support (Experimental)

### **January 31, 2023 - 14.2.3**

Image digests:

```
tines/tines-app: sha256:51a00530978f2b39ae76135c52e762931dbe673f359fa7918f1f5dff60317774
tines/tines-nginx: sha256:cbc68f16be1c664010bf0ce1932a1d8a26512171f8ef29a582eca97f868379d5
```

**Changes**

- Fixes a schema migration issue that was preventing some users from upgrading

### **January 27, 2023 - 14.1.1**

Image digests:

```
tines/tines-app: sha256:e54e0302a89702b4270a3e1a2d02a05d28d8ba729992668ee2b8f32b117805c7
tines/tines-nginx: sha256:30edb6e9efb371ab20d3831fa3b581115c2d6a94ac5a069d14cfdb6a78b236e8
```

**Changes**

- Security patches
- Fixes an issue with stories under change management being locked

### **January 27, 2023 - 14.2.2**

Image digests:

```
tines/tines-app: sha256:0c3df38d28d5433b4fb8c286c19dcfb0c519724809b3ae8c65707375265b580c
tines/tines-nginx: sha256:cbc68f16be1c664010bf0ce1932a1d8a26512171f8ef29a582eca97f868379d5
```

**Changes**

- Security patches
- Fixes an issue with stories under change management being locked

### **January 23, 2023 - 14.2.0**

Image digests:

```
tines/tines-app: sha256:a040471b9543112bbf74a3988fa69444ac9e48b64f634c3eaaf6f2d012eedaa6
tines/tines-nginx: sha256:cbc68f16be1c664010bf0ce1932a1d8a26512171f8ef29a582eca97f868379d5
```

Changes

- [New `INDEX_OF` function](https://www.tines.com/whats-new/new-index-of-function)
- **Telemetry data updates**: Improved structure of telemetry data and added additional fields sent by self hosted tenants to Tines. This data is now separated into 2 groups: 
  
  - **Required data**: This includes information about the current installed version, licence settings and license utilisation. Tenants can no longer opt out of sending this data.
  - **Opt out data**: This includes additional metadata sent to Tines such as a list of teams, stories, active feature flags and extra debug fields. This information is useful to Tines to understand utilisation and help debug issues with your tenant. You can opt-out of sending this data by enabling the feature flag `telemetry_disabled` in advance of upgrading to this version. 
  - You can visit `/admin/telemetry` on your tenant to view the exact data being sent to Tines, and enable or disable the sending of optional data. 

### **January 13, 2023 - 13.3.3**

Image digests:

```
tines/tines-app: sha256:e803c56ecb5bdc32be39b648a7169fb64758450ba98fae1a0fb768ea18045550
tines/tines-nginx: sha256:891a531e563b1c771d3a8de02ec673af589153342d4d23d4e12ecfc5d77a447f
```

**Changes**

- Fixes long load times due to failed product update requests 

### January 10, 2023 - 14.1.0

Image digests:

```
tines/tines-app: sha256:abc51bb8c3de0c3171b317ab58cac8bd7c1bf6a1b5e8975e0f2ff480bc824265
tines/tines-nginx: sha256:2d75516c8b379c57b9acec9c34592848337ef996d0fa0f78508fc2cb926a4423
```

**Changes**

- [Action Descriptions](https://www.tines.com/whats-new#action-descriptions)
- [Keyboard shortcuts directory](https://www.tines.com/whats-new#keyboard-shortcuts-directory)
- [Time-based implode](https://www.tines.com/whats-new#time-based-implode)
- ['Editor' role for teams](https://www.tines.com/whats-new#editor-role-for-teams)
- [Live events in test stories](https://www.tines.com/whats-new#live-events-in-test-story)
- [Easier event data navigation](https://www.tines.com/whats-new#easier-event-data-navigation)
- [Configurable session timeouts](https://www.tines.com/whats-new#configurable-session-timeouts)
- [API endpoint for clearing action memory](https://www.tines.com/whats-new#api-endpoint-for-clearing-action-memory)

### **December 16, 2022 - 14.0.3**

Image digests:

```
tines/tines-app: sha256:ae629ec644e7a732fa5329fbfa5b45fe883900a4c01fb6a00d469b086f0dc5df
tines/tines-nginx: sha256:2d75516c8b379c57b9acec9c34592848337ef996d0fa0f78508fc2cb926a4423
```

> **NOTE:**
> **Redis version compatibility**
> 
> From this version on, Tines requires a minimum Redis version of 6.2. For details on how to upgrade AWS Fargate installations please see [here](https://www.tines.com/docs/self-hosting/aws-fargate/upgrading-tines-for-aws-fargate#upgrading-the-redis-version), and for Docker Compose installations please see [here](https://www.tines.com/docs/self-hosting/docker-compose/upgrading-tines-on-docker-compose#upgrading-redis).

**Changes**

- Security fixes

### **December 15, 2022 - 14.0.2**

Image digests:

```
tines/tines-app: sha256:d91d4952bcbe11b9fee2bdbb429154de69df50edad4e32daa17bc8cfc3023f53
tines/tines-nginx: sha256:2d75516c8b379c57b9acec9c34592848337ef996d0fa0f78508fc2cb926a4423
```

> **NOTE:**
> **Redis version compatibility**
> 
> From this version on, Tines requires a minimum Redis version of 6.2. For details on how to upgrade AWS Fargate installations please see [here](https://www.tines.com/docs/self-hosting/aws-fargate/upgrading-tines-for-aws-fargate#upgrading-the-redis-version), and for Docker Compose installations please see [here](https://www.tines.com/docs/self-hosting/docker-compose/upgrading-tines-on-docker-compose#upgrading-redis).

**Changes**

- [Pages](https://www.tines.com/whats-new/introducing-pages)
- [Audit logs user interface](https://www.tines.com/whats-new/audit-logs-user-interface)
- [Schedule with cron expression](https://www.tines.com/whats-new/schedule-with-cron-expressions)
- [New LEVENSHTEIN_DISTANCE function](https://www.tines.com/whats-new/new-levenshtein-distance-function)
- [Keyboard shortcuts directory](https://www.tines.com/whats-new/keyboard-shortcuts-directory)
- Fix to change control toggle not being toggleable
- Security fixes



### **December 2, 2022 - 13.3.2**

Image digests:

```
tines/tines-app: sha256:6bc00fffe4bbac1306b629999265fdc72e18760568dac5a6126f1bc69b8dd606
tines/tines-nginx: sha256:2d75516c8b379c57b9acec9c34592848337ef996d0fa0f78508fc2cb926a4423
```

**Changes**

- Fixes an issue for failed product update requests



### **December 2, 2022 - 12.4.4**

> **NOTE:** This is the last release to support Liquid. Tenants will have to be migrated to Formulas before upgrading, after this release.

Image digests:

```
tines/tines-app: sha256:3f4e4001a21a578de0147ed03ed2c280855f5d70ef69af96e0c08cb49322e7ec
tines/tines-nginx: sha256:d5d053fa3da9182020f58546d7a5d4fb4468cbff868a47144047e06f2a2c48be
```

**Changes**

- Fixes an issue for failed product update requests



### **November 30, 2022 - 13.3.1**

Image digests:

```
tines/tines-app: sha256:fb0e0588fe41c4222ff1b9256d89465172c39de476cb1933aeac1195d729caed
tines/tines-nginx: sha256:2d75516c8b379c57b9acec9c34592848337ef996d0fa0f78508fc2cb926a4423
```

**Changes**

- [Change Control push messages](https://www.tines.com/whats-new/change-control-promote-messages)
- [Simplified team user management page](https://www.tines.com/whats-new/simplified-team-user-management-page)
- [‘Viewer’ role for teams](https://www.tines.com/whats-new/viewer-role-for-teams)
- [See resources, credentials, and send-to-stories shared with your team](https://www.tines.com/whats-new/see-resources-credentials-and-send-to-stories-shared-with-your-team)

### **November 17, 2022 - 13.2.1**

Image digests:

```
tines/tines-app: sha256:6c827631fd8f602f08d97a1fca0b3713e090b61b6e8cf144e08487aaff4ccca8
tines/tines-nginx: sha256:2d75516c8b379c57b9acec9c34592848337ef996d0fa0f78508fc2cb926a4423
```

**Changes**

- [AWS credential caching](https://www.tines.com/whats-new/aws-credential-caching)
- [Share resources, credentials, and send-to-stories to specific teams](https://www.tines.com/whats-new/share-resources-credentials-and-send-to-stories-to-specific-teams)
- [Capture creator in Story versions](https://www.tines.com/whats-new/capture-creator-in-story-versions)
- [Advanced formula editor](https://www.tines.com/whats-new/advanced-formula-editor)
- [Smoother login for self-hosted tenants](https://www.tines.com/whats-new/smoother-login-for-self-hosted-tenants)
- [Credential icons](https://www.tines.com/whats-new/credential-icons)
- [New `PARSE_URL` function](https://www.tines.com/whats-new/new-parse-url-formulas-function)
- [New `REJECT` function](https://www.tines.com/whats-new/new-reject-function)
- [Surface all story resources & credentials](https://www.tines.com/whats-new/surface-all-story-resources-credentials)
- [Dynamic form field options](https://www.tines.com/whats-new/dynamic-form-field-options)
- [Default values for form fields](https://www.tines.com/whats-new/default-values-for-form-fields)
- Fixes issue with 13.2.0 that changed the published state on certain stories

### **November 2, 2022 - 12.4.3**

Image digests:

```
tines/tines-app: sha256:6109e4144bbd67d27b0b377d29ed99f316278bfd30b5d1d30c05e08d52d4e4c3
tines/tines-nginx: sha256:2e4b681191981045cda39788a51851c65a2dfa8ae3e4882998b844275c6c185e
```

**Changes**

- Fix an issue when using binary data in a second interpolation pass
  
  

### **October 31, 2022 - 13.1.0**

Image digests:

```
tines/tines-app: sha256:281c47e70d4476c291252cd50e5311cbcc7269de2cd49d753d283ba119953245
tines/tines-nginx: sha256:e3f7af237ab2779b8d21903d706a3a56514805c67e2c16e41f0ad2ee6fe7dbb9
```

**Changes**

- [Service Account API Tokens](https://www.tines.com/whats-new/service-account-api-tokens)
- [Throttle mode for Event Transformation Action](https://www.tines.com/whats-new/throttle-mode-for-event-transformation-action)
- [Increased legibility in the events panel](https://www.tines.com/whats-new/increased-legibility-in-the-events-panel)
- [Pill builder auto save](https://www.tines.com/whats-new/builder-auto-saving)
- [A new level of flexibility for forms](https://www.tines.com/whats-new/a-new-level-of-flexibility-for-forms)
- [Collapsible sidebar](https://www.tines.com/whats-new/collapsible-sidebar)
- [Formula results tooltip](https://www.tines.com/whats-new/formula-results-tooltip)
- [New FLATTEN/UNFLATTEN_JSON Formula functions](https://www.tines.com/whats-new/new-flatten-unflatten-json-formula-functions)
- [User logins included in audit logs](https://www.tines.com/whats-new/logins-in-audit-logs)
- [New CHUNK_ARRAY Formulas function](https://www.tines.com/whats-new/new-chunk-array-formulas-function)
- Dockerfile changes
  
  - Copy gems directly into its dedicated location
  - Reduce `sleep` time in Dockerfile

### **October 27, 2022 - 13.0.3**

Image digests:

```
tines/tines-app: sha256:3620d9a10ef678f37c92b78b83be53bf0e38c40a54b67491c97e6be1c7cef786
tines/tines-nginx: sha256:5b902caded53a0581d7abd398ebf8f246777651e0301b68f693d252e3e60303a
```

**Changes**

- Fixed database issues after attempting to upgrade to v13 without Formulas being enabled
- Fixed issues saving credentials

### **October 20, 2022 - 13.0.2**

Image digests:

```
tines/tines-app: sha256:ff652b9897378dc77dad2e5690d2c198d9d1788c50a244414fd48ca4f365423c
tines/tines-nginx: sha256:3a5dc79aa21880621724ab69b934b54aac7fdb1bcd750b1cf7a057717c9d0e7d
```

**Changes**

- Fixes issue with deleting users and their stories not being listable
- Security fixes

### **October 18, 2022 - 13.0.0**

Image digests:

```
tines/tines-app: sha256:095d2c06aeb0c50f7344f6e3c5721ab60a57dc777ae1ba8f8d63f913239b14b5
tines/tines-nginx: sha256:3a5dc79aa21880621724ab69b934b54aac7fdb1bcd750b1cf7a057717c9d0e7d
```

**Changes**

- [Direct access to audit logs for self-hosted tenants](https://www.tines.com/whats-new/direct-access-to-audit-logs-for-self-hosted-tenants)
- [Restrict credentials to multiple domains](https://www.tines.com/whats-new/restrict-credentials-to-multiple-domains)
- [New ](https://www.tines.com/whats-new/new-values-formula-function)`VALUES`[ Formula function](https://www.tines.com/whats-new/new-values-formula-function)
- [Change Control](https://www.tines.com/whats-new/change-control)
- [OAuth 2.0 authentication support for IMAP](https://www.tines.com/whats-new/oauth-2-0-support-for-imap)
- [Custom retry limits for HTTP Request Actions](https://www.tines.com/whats-new/custom-retry-limits-for-http-request-actions)

### **October 18, 2022 - 12.4.2**

Image digests:

```
tines/tines-app: sha256:54283664ab14331005082358c772abf24ffe3e88650a0c9bb81a74272ca0e5f7
tines/tines-nginx: sha256:3a5dc79aa21880621724ab69b934b54aac7fdb1bcd750b1cf7a057717c9d0e7d
```

**Changes**

- Fixes issue with Forms on storyboard migration

### **October 5, 2022 - 12.4.1**

Image digests:

```
tines/tines-app: sha256:ace4ab7f0defa6957088f55aba2cf1fe4c8dfa7082b668d223a2ded11c36fb03
tines/tines-nginx: sha256:274b7eeaf3f702424213a773cdeecc4be543287d22a5d5da03f694ff0d12251b
```

**Changes**

- [OAuth 2.0 authentication support for IMAP](https://www.tines.com/whats-new/#oauth-2-0-support-for-imap)
- [Custom retry limits for HTTP Request Actions](https://www.tines.com/whats-new/#custom-retry-limits-for-http-request-actions)
- Fix an issue where actions with invalid `standardLibVersion` cannot be pasted
- Fix an issue where newly created webhooks crashes the diagram's right panel

### **October 4, 2022 - 12.4.0**

Image digests:

```
tines/tines-app: sha256:43d6c26586aef9bf3e414ad314147a03ca078bf2b462045871b5246a8bfe716e
tines/tines-nginx: sha256:274b7eeaf3f702424213a773cdeecc4be543287d22a5d5da03f694ff0d12251b
```

**Changes**

- [Private group templates](https://www.tines.com/whats-new/private-group-templates)
- [No-config Slack credentials](https://www.tines.com/whats-new/no-config-slack-credentials)
- [New DEEP_MERGE Formula function](https://www.tines.com/whats-new/new-deep-merge-formula-function)
- [New XML_PARSE Formula function](https://www.tines.com/whats-new/new-xml-parse-formula-function)
- [New LDIF_PARSE Formula function](https://www.tines.com/whats-new/new-ldif-parse-formula-function)
- [Action cards UI refresh](https://www.tines.com/whats-new/action-cards-ui-refresh)
- [Forms on the Storyboard](https://www.tines.com/whats-new/forms-on-the-storyboard)
- [Domain restricted Credentials](https://www.tines.com/whats-new/domain-restricted-credentials)
- [Automatic URLs for common OAuth providers](https://www.tines.com/whats-new/automatic-urls-for-common-oauth-providers)

### **September 21, 2022 - 12.3.1**

Image digests:

```
tines/tines-app: sha256:51de118708045181017cc791a270afd7adcbdf64b46a8a04b160826e56ba03c4
tines/tines-nginx: sha256:274b7eeaf3f702424213a773cdeecc4be543287d22a5d5da03f694ff0d12251b
```

**Changes**

- This patch release contains a fix for adding AWS credentials.

### **September 19, 2022 - 12.3.0**

Image digests:

```
tines/tines-app: sha256:1954a7ec7b215a3b3185387f5670a1e96e75c9822281b42680a59295e8b3d5f6
tines/tines-nginx: sha256:274b7eeaf3f702424213a773cdeecc4be543287d22a5d5da03f694ff0d12251b
```

**Changes**

- [PKCE Support for OAuth 2.0](https://www.tines.com/whats-new/pkce-support-for-oauth-2-0)
- [New folder icons and more color options](https://www.tines.com/whats-new/new-folder-icons-and-more-color-options)

### **September 6, 2022 - 12.2.1**

Image digests:

```
tines/tines-app: sha256:d266cceeb26f4e0e5b3f970b1f0f31618309101f6e246d061e2118779c44a6d0
tines/tines-nginx: sha256:274b7eeaf3f702424213a773cdeecc4be543287d22a5d5da03f694ff0d12251b
```

**Changes**

- [Detachable Events Console](https://www.tines.com/whats-new/detachable-events-console)
- [Time Saved API](https://www.tines.com/whats-new/time-saved-api)

### **August 31, 2022 - 12.1.1**

Image digests:

```
tines/tines-app: sha256:fbdbc8e84cb516abc69cfeb8e825baf0f7cc03d14207bffdd0ef0d85c61a45c4
tines/tines-nginx: sha256:274b7eeaf3f702424213a773cdeecc4be543287d22a5d5da03f694ff0d12251b
```

**Changes**

Security updates to handle the following bugs:

- [CVE-2022-37434](https://security-tracker.debian.org/tracker/CVE-2022-37434)
- [CVE-2022-2509](https://security-tracker.debian.org/tracker/CVE-2022-2509)
- [CVE-2022-36946](https://security-tracker.debian.org/tracker/CVE-2022-36946)
- [CVE-2022-1882](https://security-tracker.debian.org/tracker/CVE-2022-1882)
- [CVE-2021-46828](https://security-tracker.debian.org/tracker/CVE-2021-46828)
- [CVE-2022-29900](https://security-tracker.debian.org/tracker/CVE-2022-29900)
- [CVE-2022-1462](https://security-tracker.debian.org/tracker/CVE-2022-1462)
- [CVE-2022-36879](https://security-tracker.debian.org/tracker/CVE-2022-36879)
- [CVE-2022-29901](https://security-tracker.debian.org/tracker/CVE-2022-29901)

### **August 23, 2022 - 12.1.0**

Image digests:

```
tines/tines-app: sha256:39642b453a75fb30823d2cab1b1086ea6d247596f37276b979f3aceb79e73a19
tines/tines-nginx: sha256:274b7eeaf3f702424213a773cdeecc4be543287d22a5d5da03f694ff0d12251b
```

**Changes**

- [New SHUFFLE Formula function](https://www.tines.com/whats-new/new-shuffle-formula-function)
- [Search team members](https://www.tines.com/whats-new/search-team-members)
- [Reporting drill-down filters](https://www.tines.com/whats-new/reporting-drill-down-filters)
- [Call Webhook Actions from the Web](https://www.tines.com/whats-new/call-webhook-actions-from-the-web)
- [Support for NTLM authentication](https://www.tines.com/whats-new/support-for-ntlm-authentication)
- [New CSV_PARSE_TO_OBJECTS Formula function](https://www.tines.com/whats-new/new-csv-parse-to-objects-formula-function)
- [Deleting and restoring Stories](https://www.tines.com/whats-new/deleting-and-restoring-stories)
- [Support for ES256 JWT credentials](https://www.tines.com/whats-new/support-for-es256-jwt-credentials)
- [Less friction in unpublished Stories](https://www.tines.com/whats-new/less-friction-in-unpublished-stories)
- [Simplified navigation](https://www.tines.com/whats-new/simplified-navigation)
- [New REGEX_EXTRACT Formula function](https://www.tines.com/whats-new/new-regex-extract-formula-function)
- [Redirect authorization in HTTP Request Action](https://www.tines.com/whats-new/redirect-authorization-in-http-request-action)

### **August 11, 2022 - 12.0.3**

Image digests:

```
tines/tines-app: sha256:4e09f070eff0aebeda345e1f41879e98beb53c9992c6a5941a0dc2b6d1906027
tines/tines-nginx: sha256:e721189f4697ba5b845ba46f4904379769dbc5dd605b6468753b10f7cdd93e8b
```

**Changes**

- Beta release of NTLM auth
- Fix issue related to the tooling for Formulas migrations

### **August 9, 2022 - 12.0.2**

Image digests:

```
tines/tines-app: sha256:ee823677cc91bfa25f0a2e270b7d4bbd4663b772811764d12a69b984ce370457
tines/tines-nginx: sha256:e721189f4697ba5b845ba46f4904379769dbc5dd605b6468753b10f7cdd93e8b
```

**Changes**

- Fix issue related to the tooling for Formulas migrations

### **August 8, 2022 - 12.0.1**

Image digests:

```
tines/tines-app: sha256:30192d2df7c65fd9e838f81dfc489e8cf06d9776c3a8011ebe574f8dea4603c2
tines/tines-nginx: sha256:e721189f4697ba5b845ba46f4904379769dbc5dd605b6468753b10f7cdd93e8b
```

**Changes**

- [New events panel](https://www.tines.com/whats-new/unveiling-the-new-events-panel)
- [Receive email action](https://www.tines.com/whats-new/receive-email-action)
- [Automatic cleanup of action logs](https://www.tines.com/whats-new/automatic-cleanup-of-action-logs)
- [Story list presence](https://www.tines.com/whats-new/story-list-presence)
- [Searchable annotations](https://www.tines.com/whats-new/search-annotations)
- [Pills in JWT credential payload](https://www.tines.com/whats-new/pills-in-jwt-credential-payload)
- [Re-emit an actions last event](https://www.tines.com/whats-new/re-emit-last-event)
- [HTTP request credentials no-code mode](https://www.tines.com/whats-new/no-code-comes-to-http-request-credentials)
- [Move credentials & resources between teams](https://www.tines.com/whats-new/move-credentials-and-resources-between-teams)
- [Audit logs - enriched user information](https://www.tines.com/whats-new/audit-logs-enriched-user-information)

### July 26, 2022 - 11.3.2


Image digests:

```
tines/tines-app: sha256:7b75c2f00f45c17ec6177a235da46743891f0870ff8c22433ac01799edc02314
tines/tines-nginx: sha256:e721189f4697ba5b845ba46f4904379769dbc5dd605b6468753b10f7cdd93e8b
```

#### Changes

- Fix login issue related to avatars
- Improved Formulas migration support

### July 25, 2022 - 11.3.1


Image digests:

```
tines/tines-app: sha256:0be9cb4437e0069bec7ddb9cdd04fc0a913302f21678f955e3ccf0a921114155
tines/tines-nginx: sha256:e721189f4697ba5b845ba46f4904379769dbc5dd605b6468753b10f7cdd93e8b
```

#### Changes

- [Tines Tunnel for IMAP Actions](https://www.tines.com/whats-new#tines-tunnel-for-imap-actions)
- [Left Panel Redesign](https://www.tines.com/whats-new#left-panel-redesign)
- [Indicator for throttled stories](https://www.tines.com/whats-new#indicator-for-throttled-stories)

### July 11, 2022 - 11.2.0


Image digests:

```
tines/tines-app: sha256:6e9c4171b7d3040ac95ed8f36d98d4ec54f730f43c673445b0cb5b32a3f424e8
tines/tines-nginx: sha256:339fb41eb5b3450f95c6245aaf6a83dc739a6a309ea0f19d892d16ff8e545604
```

#### Changes

- Fix story resources and creds not updating
- Fix bug in event re-emission
- [Story and Group rename from the toolbar](https://www.tines.com/whats-new#inline-name-change-in-a-toolbar)
- [Public group templates](https://www.tines.com/whats-new#inline-name-change-in-a-toolbar)
- [Updated form builder](https://www.tines.com/whats-new#public-group-templates)

### June 27, 2022 - 11.1.0 

Image digests:

```
tines/tines-app: sha256:d1b885140493c250a00bcd02b76d02cd552c56b26c57f0d81e41835fbef85e9f
tines/tines-nginx: sha256:339fb41eb5b3450f95c6245aaf6a83dc739a6a309ea0f19d892d16ff8e545604
```

#### Changes

- [Audit Log API for admins](https://hub.tines.com/whats-new#audit-logs)
- [Story locking to prevent accidental changes](https://hub.tines.com/whats-new#story-locking)
- [Undo story deletion to restore recently deleted stories](https://hub.tines.com/whats-new#undo-story-deletion)
- [New MATCH & KEYS Formulas Functions](https://hub.tines.com/whats-new#match--keys-formulas-functions)
- [Visible OAuth error messages](https://hub.tines.com/whats-new#visible-oauth-error-messages)
- [Copy HTTP Request as cURL](https://hub.tines.com/whats-new#copy-http-request-as-curl)
- [Customize sender name in Email Action](https://hub.tines.com/whats-new#customize-sender-name-in-email-action)
- [Resources on the storyboard](https://hub.tines.com/whats-new#resources-on-the-storyboard)

### June 15, 2022 - 11.0.1

Image digests:

```
tines/tines-app: sha256:b0aa7957332be28bba05a633bf60a1ca0514f8b6a5092a82491c3ec8c4939561
tines/tines-nginx: sha256:339fb41eb5b3450f95c6245aaf6a83dc739a6a309ea0f19d892d16ff8e545604
```

#### Changes

- This patch release contains fix for a set of database schema changes that can take potentially longer on some self-hosted tenants.

### June 13, 2022 - 11.0.0

```
tines/tines-app: sha256:7c226228bf9f2e6c2f65b9633dfe371b1dbffd4c5e941722085ae823be939775
tines/tines-nginx: sha256:339fb41eb5b3450f95c6245aaf6a83dc739a6a309ea0f19d892d16ff8e545604
```

#### Changes

- [A new UUID function](https://hub.tines.com/whats-new/#uuid-function)
- [Clickable action event badges](https://hub.tines.com/whats-new/#clickable-action-event-badges)
- [Refresh OAuth tokens directly from the credential modal](https://hub.tines.com/whats-new/#refresh-oauth-token-button)
- [View and update credentials from within a story](https://hub.tines.com/whats-new/#credential-details)
- [Reporting – Record metrics for actions and view reports](https://hub.tines.com/whats-new/#reporting)

### May 31, 2022 - 10.3.1

```
tines/tines-app: sha256:69c48fd38826bb3e752107b09d15b9a0a6eae1ae96f0878c54f6b453f05fb4c6
tines/tines-nginx: sha256:339fb41eb5b3450f95c6245aaf6a83dc739a6a309ea0f19d892d16ff8e545604
```

#### Fix TLS issues with SMTP servers

Fixed issues where customers that have disabled TLS on their custom SMTP servers could not send emails.

### May 30, 2022 - 10.3.0

Image digests:

```
tines/tines-app: sha256:33b2f984b454b17cea8f6d1462c535fa67c2185ff7f70b3389a7072da142da13
tines/tines-nginx: sha256:339fb41eb5b3450f95c6245aaf6a83dc739a6a309ea0f19d892d16ff8e545604
```

#### General improvements

This release focuses on various usability, security and performance improvements.

### May 16, 2022 - 10.2.0

Image digests:

```
tines/tines-app: sha256:e5eaf45bee6d2f0b5a51d734a974ffb8a60de9a601687970d49d57eb1cbf83c5
tines/tines-nginx: sha256:eac3bd5a26d4aea8b7bae67a17163791a76aee8861d131e0c510aac2ed81e008
```

#### Resizable Annotations

You can now change the width of an annotation to better fit the content and your story.

*Resizable annotations*

#### Event Payload Search Term Highlighting

Relevant search term from Event Payload Search will now be highlighted on match.

![](https://www.datocms-assets.com/55802/1656586756-2022-05-10-eps-11f28d5eeb31affc7c1f77778afd80e3.png)

#### Event Payload Search

You can now perform full text search within all event payloads.

![](https://www.datocms-assets.com/55802/1656586781-2022-05-03-eps-2d6618ed71b61615f7ef3f104fe40435.png)

#### Other Teams View

Admins can now access teams they are not members of through the **Other Teams** list, located below the **My Teams** list on the left-hand navigation bar.

![](https://www.datocms-assets.com/55802/1656586820-2022-05-03-other-teams-b1419a2b8eeea9fbff0c2451be4988cc.png)

### May 05, 2022 - 10.1.1

> **NOTE:** Improved support for Docker image changes made by self-hosting customers.

```
tines/tines-app: sha256:d24f20d6a2fe32bff3681c406ec15afaf95db62d3807f09127099a3324d04914
tines/tines-nginx: sha256:eac3bd5a26d4aea8b7bae67a17163791a76aee8861d131e0c510aac2ed81e008
```

### May 03, 2022 - 10.1.0

> **NOTE:** We've updated our supported Ruby version to 3.1.2. We now base the `tines-app` container image on the the official [`ruby:3.1.2-slim-bullseye`](https://hub.docker.com/_/ruby) image.

Image digests:

```
tines/tines-app: sha256:c016d4e8f9486b9bbc2d85efaf5017da574959b19bc368833fd807ee171e6995
tines/tines-nginx: sha256:eac3bd5a26d4aea8b7bae67a17163791a76aee8861d131e0c510aac2ed81e008
```

#### Easier access to auto-align and auto-layout on the action bar

You can now access auto-layout and auto-align directly from the action bar.

![](https://www.datocms-assets.com/55802/1656586887-2022-04-29-auto-align-auto-layout-action-bar-24f2b5dad354123e16de9bf1e1a05e6f.png)

#### Search users

You can now search a user by their name or email address from the user management page.

![](https://www.datocms-assets.com/55802/1656586903-2022-04-22-users-search-a1c39143c7d36461827c72c733e2c48c.png)

#### Show impact of second Liquid interpolation pass for self-hosting customers

Self-hosted customers can now see the impact that a second Liquid interpolation pass is having on their actions by visiting `<tenant-domain/admin/upgrade`. This behaviour is deprecated and will be removed in a future release.

- If you have been running 10.1.0 or a later version for longer than a couple of days, you can see which actions have been affected by the behaviour by visiting that page. The page will give further context and instructions on how to disable the behaviour.
- If the section about this behaviour does not show up on the page, then you have no actions that have been affected. If this is the case, you don't need to take any further action.

![](https://www.datocms-assets.com/55802/1656586925-second-pass-2022-05-06-ddce491b095a2a5396d1cd9cabd7340a.png)

### April 19, 2022 - 10.0.0

Image digests:

```
tines/tines-app: sha256:8327dfbe6f08ba5fdd1d86cd7c4c5ecef64249be1fd40ed344a202bc8f290427
tines/tines-nginx: sha256:eac3bd5a26d4aea8b7bae67a17163791a76aee8861d131e0c510aac2ed81e008
```

#### Reemit API

You can now re‑emit an event through the [Tines API](https://hub.tines.com/api/events/reemit). Re‑emitting events duplicates them and passes them down to receiving actions. [Learn more](https://hub.tines.com/docs/events#reemit-events)

### April 4, 2022 - 9.3.1

Image Digests:

```
tines/tines-app: sha256: 5a2bf245c880f3f76d3db145514bd54bc9565ccb6991fa136bbe05477396253a
tines/tines-nginx: sha256: c950c7f321753322fb3cf4e93bea6441f0c339990693cbf3580b467d0e9aac78
```

#### Removing plan limit environment variables

New self-hosted installations will now use default initial plan limits. As before, these can be updated to the correct limits from your plan through the `/admin/configuration` page on your tenant. Correspondingly, the `USER_LIMIT`, `AGENT_LIMT`, `EVENTS_PER_DAY`, `STORY_LIMIT` and `TEAM_LIMIT` environment variables are totally ignored by the app, and can be removed from your `.env` file.

#### Groups

Groups enable a better organization of complex stories by extracting well-defined branches of a story into a group. [Learn more](https://hub.tines.com/docs/stories/groups)

**

#### Select data to implode

Event Transformation Actions in "implode" mode have a new option: "Item path". Using this option, you can select the data from the events being imploded to include in the output event.

![](https://www.datocms-assets.com/55802/1656586983-2022-03-24-item-path-options-e46f1f8d8fd0863bcbc55e8a6eeebc06.png)

![](https://www.datocms-assets.com/55802/1656587002-2022-03-24-item-path-event-9a01e5b5c26b5c57b1220a1ddff39b1d.png)

#### Clear events from the action bar

You can now clear events for the selected action from the action bar.

![](https://www.datocms-assets.com/55802/1656587022-2022-03-23-event-deletion-in-action-bar-edad3cf51feed68a1a513080805de7ed.png)

### March 21, 2022 - 9.2.0

Image Digests:

```
tines/tines-app: sha256:32fbc91dcddd95dd4b398fa8911d8e16a9e76bafc47d437c04d74409720f6c0a
tines/tines-nginx: sha256:c950c7f321753322fb3cf4e93bea6441f0c339990693cbf3580b467d0e9aac78
```

#### Credentials on the storyboard

4 new features to help you more efficiently manage credentials in your stories.

1. List: You will now see a list of the credentials that are referenced throughout your story in the sidebar.
2. Missing Credentials: will be surfaced in the credentials list.
3. Reference Overview: You will be able to see which actions reference the credential in both existing and missing credentials.
4. Story wide replace: You will be able to replace your credentials story wide in just a few clicks.

**

#### Create credentials from the storyboard

You can now create credentials from the storyboard.

**

#### Copy event key path as pill

Double click on a key in an event payload to copy its path as a pill.

**

### March 7, 2022 - 9.1.0

Image Digests:

```
tines/tines-app: sha256:c3a854c46a148ab328688e913ff9bf74451fa7b6480b5c63d67603f64663818e
tines/tines-nginx: sha256:c950c7f321753322fb3cf4e93bea6441f0c339990693cbf3580b467d0e9aac78
```

#### Auto align and Auto layout story actions

2 new features to help you arrange actions in your stories.

1. Auto-align: Will snap selected items to a grid and ensure that items are equally spaced out for neatness
2. Auto-layout: Will reflow the selected actions based on the current links

**

#### Custom certificate authority

You can now [configure a custom certificate authority](https://hub.tines.com/docs/admin/custom-certificate-authority) for use by all of your IMAP and HTTP Request actions.

![](https://www.datocms-assets.com/55802/1656588520-2022-03-01-custom-certificate-authority-c618a9f8fcae9ee03620b91f037d5d95.png)

#### URL recipients for action monitoring

You can now set a URL as an action monitoring recipient.

![](https://www.datocms-assets.com/55802/1656588533-2022-02-25-webhook-monitor-497becd37a4cb829072294fcc53a50e5.png)

### February 21, 2022 - 9.0.0

#### Story presence

Collaboration within a story is now much easier: see when someone else on your team is viewing the same story and follow along as you both work through your stories together.

**

#### Email action Reply-To option

You can now specify a Reply-To address when configuring an email action.

![](https://www.datocms-assets.com/55802/1656588571-2022-02-17-reply-to-11612be47671edd99545032413ee022d.png)

#### Trigger action editor

The interface for configuring Trigger actions has been revised to make it read more naturally.

![](https://www.datocms-assets.com/55802/1656588586-2022-02-17-trigger-ui-3bf6240bfc228799c9bbb56faba98a10.png)

#### is_ip_address Liquid filter

A `is_ip_address` Liquid filter has been added. It checks if a piece of text is a valid IP address.

`{{ "10.0.0.1" | is_ip_address}}` evaluates to true

`{{ "10.0.0.888" | is_ip_address }}` evaluates to false

#### Pill drag and drop

Our options editor now supports cutting, pasting, dragging and dropping value and tag pills.

![](https://www.datocms-assets.com/55802/1656588610-2022-02-09-drag-and-drop-415e87c17b6036b25a40840767c09243.gif)

#### Action bar

The controls for running, testing, copying and deleting an action are now shown in a new action bar beside an action when it's selected.

**

Image Digests:

```
tines/tines-app: sha256:253030844927af49487d930cec983477688d74ce709d92677c1f7c4f34de61b1
tines/tines-nginx: sha256:0a02365870a2fd30ce3046b6f685ab5ca8f439179c31336bccb060077acddb12
```

### February 7, 2022 - 8.3.0

#### See usage when deleting a credential

Now you can see which stories and actions use a credential before deleting it. You can click on the listed actions to open the story, centred on that action, in a new tab. Also, you can now delete a credential even if it’s being used in an action without having to delete the action first.

![](https://www.datocms-assets.com/55802/1656588662-delete-credential-f4c7f2f79fc2e7a991220c8533ece512.png)

#### Story credentials list

You can now see which credentials are being used in a story, as well as any missing credentials that the story is referencing.

![](https://www.datocms-assets.com/55802/1656588680-story-credential-list-8825faac8775424de99a7f5ceb5178e2.png)

Image Digests

```
tines/tines-app: sha256:651b38a5f31760c6620481646366a266d428ae2e8e511e3bbb312fdd647ff0e5
tines/tines-nginx: sha256:0a02365870a2fd30ce3046b6f685ab5ca8f439179c31336bccb060077acddb12
```

### January 24, 2022 - 8.2.0

Self-hosted customers can now download ZIP files for releases through cloud tenants at the path `/admin/upgrade`. Contact Tines support to get this enabled for your cloud tenant. If you don’t have a cloud tenant, you can sign up for a free Community Edition tenant at [tines.com](http://tines.com/).

![](https://www.datocms-assets.com/55802/1656588706-untitled-fc44a6a727ac8a1ec14b9f559cb4579f.png)

#### Randomize webhook secrets on export

When exporting a story, you can now check a checkbox to randomize the webhook secrets keeping your webhooks unique upon re-importing.

![](https://www.datocms-assets.com/55802/1656588721-untitled-1-12185ac2e978fd20fa07651a5da85e7a.png)

#### Breadcrumb navigation

**

Image Digests:

```
tines/tines-app: sha256:844cbddeaad894cbc01fb611140a1467b99d54d5f4a47d09977ac10c29fcf0d2
tines/tines-nginx: sha256:0a02365870a2fd30ce3046b6f685ab5ca8f439179c31336bccb060077acddb12
```

### January 10, 2022 - 8.1.0

#### Re-skinned Registration / Login flows

![](https://www.datocms-assets.com/55802/1656588808-untitled-2-bae291193a937c835f10648a36115345.png)

![](https://www.datocms-assets.com/55802/1656588826-untitled-3-d1e95a39b06e13cc729252178467d84f.png)

![](https://www.datocms-assets.com/55802/1656588861-untitled-4-3d3edef909d401567201f6e1031d4cce.png)

![](https://www.datocms-assets.com/55802/1656588873-untitled-5-609e5c5c382617c76975475d6cc9ef39.png)

#### New Liquid Editor

The new liquid editor is now generally available for all tenants

[No-code Development: Introducing the New Liquid Editor | Tines](https://www.tines.com/blog/liquid-editing-improvements)

#### Emit all held events on publish

When publishing a story, a checkbox can now be checked, to emit all held events upon publishing. In addition to this, when a story is unpublished with held events, a button appears in the publish dropdown allowing users to emit all held events.

![](https://www.datocms-assets.com/55802/1656588892-untitled-6-d1a45d7cb37c64fa9687afac2bf93b55.png)

Image Digests

```
tines/tines-app: sha256:4f4ed744f7e42cd15976ca4441c42d0021d7edebe4068232c8ad413e7cb1332e
tines/tines-nginx: sha256:0a02365870a2fd30ce3046b6f685ab5ca8f439179c31336bccb060077acddb12
```

### December 13, 2021 - 8.0.0

> **NOTE:** This is the first 8.x release of Tines - upgrades to this version are only possible from a 7.x release. We recommend self-hosting customers upgrade to 7.3.0 before upgrading to 8.0.0 to ensure that the upgrade process is as smooth as possible.

#### Looping support for Event Transformation actions

It is now possible to configure an Event Transformation action in `message_only` mode to loop through a list or and object in a incoming event.

- Specify the path to a field in an incoming event that contains a list or an object and Tines will invoke the action for each element of the list or object.
- When specifying the output event payload, a `LOOP` object will be provided for each loop iteration. The `LOOP` object will contain:
- `value` – The current value in the loop.
- `index` – The current index in the loop.
- `key` - When iterating over key/value pairs in an object, this is the current key in the loop. This will be absent when iterating over a list.
- `previous_result` – The result of the previous iteration.
- A single output event will still be emitted.
- The payload of the output event will always be a list. It can potentially contain `NULL` elements. 

![](https://www.datocms-assets.com/55802/1656588931-image-1-1d430aa11fabc3bdc771ce9d6aa763bd.png)

#### Resource descriptions

You can now add descriptions to resources to store more information about them.

#### HS256 support for JWT credentials

JWT credentials can now use the HS256 algorithm instead of RS256.

Image digests:

```
tines/tines-app: sha256:468c47ef130eb62453fe2f777d29b170795b526b86c03a66e08fd37929db5a5d
tines/tines-nginx: sha256:0a02365870a2fd30ce3046b6f685ab5ca8f439179c31336bccb060077acddb12
```

### November 29, 2021 - 7.3.0

#### Global search

Clicking the search button now opens a new page which can search across all the stories, actions, credentials, users and resources that you have access to.

#### Credential descriptions

You can now add descriptions to credentials to store more information about them.

![](https://www.datocms-assets.com/55802/1656589174-image-f6b7c7f1f5b9801aa0019a1edc719fa7.png)

#### OIDC support for SSO

OpenID Connect is now a supported option when configuring your SSO authentication.

![](https://www.datocms-assets.com/55802/1656589187-screenshot_2021-11-29_at_13-32-55-613bae421764dda95f6b3ace027fe088.png)

#### Transliterate Liquid filter

The new `transliterate` filter replaces non-ASCII characters with an ASCII approximation, or if none exists, a replacement character: `?`.

#### Light/Dark mode hotkeys

You can now switch between light & dark mode with `Ctrl` + `Shift` + `L` (`⌘` + `Shift` + `L` on macOS).

#### Operation logging

`tines-app` container logs will now show when a user performs an operation through the UI or API that can change data, e.g. `Performing GraphQL operation 'annotationContentChangeMutation' for user ID 4`

Image digests:

```
tines/tines-app: sha256:f18e016f91b1bc3d56d59a0b6acdb37b48582ce318407f473ac42d5c5f715ef5
tines/tines-nginx: sha256:0a02365870a2fd30ce3046b6f685ab5ca8f439179c31336bccb060077acddb12
```

### November 15, 2021 - 7.2.0

#### Action configuration UI fixes

Fixed a couple of minor issues that occurred when editing an action.

Image digests:

```
tines/tines-app: sha256:f3b831e84442950e1b199e9c305202ebbc0ea15771656d5790a66140169d5d1f
tines/tines-nginx: sha256:0a02365870a2fd30ce3046b6f685ab5ca8f439179c31336bccb060077acddb12
```

### November 1, 2021 - 7.1.0

#### Inline errors in action editor

Configuration errors will now be shown inline beside the field where the error is located.

![](https://www.datocms-assets.com/55802/1656589234-image-1-1-b0624450178f680b3445f3e547b77402.png)

#### **Fully customizable webhook URLs**

Webhook actions now have a `path` option which specifies the first randomized path segment of the URL, which was previously unconfigurable.

![](https://www.datocms-assets.com/55802/1656589247-image-2-40e7c457ac9a2a129dd192ff905ff584.png)

#### Self-hosted: HTTPS connection to tines-app container

The `tines-app` container now serves HTTPS traffic on port 3001 if an SSL key pair is provided through Docker volumes at the correct paths:

```
tines-app:
  image: tines/tines-app:latest
  command: start-tines-app
  ...
  volumes:
    - ./tines-app.crt:/home/tines/tines/tines-app.crt
    - ./tines-app.key:/home/tines/tines/tines-app.key
```

Image digests:

```
tines/tines-app: sha256:7a9af8c3b1ce0abad020cbd602d3f131a337fb782a2cca22ab114eb14bb18556
tines/tines-nginx: sha256:17595e7d2e12e6fc049c3a269ef869f98a77aee566e84541f788ed0dd634cab3
```

### October 18, 2021 - 7.0.1

> **NOTE:** This is the first 7.x release of Tines - upgrades to this version are only possible from a 6.x release. We recommend self-hosting customers upgrade to 6.3.0 before upgrading to 7.0.0 to ensure that the upgrade process is as smooth as possible.

🕒 **Story versioning** 

Changes made to your stories are now stored in a version history, so you can restore old versions of a story at any time. Versions can be renamed to give them extra context, and you can export specific versions or clone them to a new story. 

![](https://www.datocms-assets.com/55802/1656589283-screenshot_2021-10-18_at_14-25-18-f8725c5f1a8a5123050d75c9a139b6ae.png)

**META variables** 

You can now use `{{ META }}` to access information about the current environment, like the name of the current team (`{{ META.team.name }}`), the ID of the current action (`{{ META.action.id }}`), or the domain of the current tenant (`{{ META.tenant.domain }}`). This makes it easier to do a range of things, like log data about action runs:

```
Send to Story triggered from action {{ META.action.id }}
```

and send requests to your tenant's API:

```
url: https://{{ META.tenant.domain }}/api/v1/stories
```

You can now manually choose how to parse responses to HTTP requests. This is useful for APIs that respond with a misleading content type, or for cases where you want to keep the unparsed text. 

![](https://www.datocms-assets.com/55802/1656589370-screenshot_2021-10-07_at_11-48-21-3e83005ebea95604a07946b0b3a92113.png)

Image digests:

```
tines/tines-app: sha256:f5a5873cede22f74e31e169a32bba244885edef2fdd130a444138b0eb13bf1e6
tines/tines-nginx: sha256:765d2e68eb52c5fc8617afe996224bdea3b1aa1bdfdba858ff26de8a060fb2db
```

### October 4, 2021 - 6.3.0

#### **More accurate “Edited at” timestamps.**

The timestamps indicating when the story was last edited are now more accurate taking into account things like action options change.

![](https://www.datocms-assets.com/55802/1656589398-image-1-209a7bcb6ec7dbb741c2de249092c19f.png)

#### **Docker security enhancements**

We’ve made a couple of security improvements to the Tines Tunnel Docker images:

- The image now uses a user [without root privileges](https://dzone.com/articles/docker-without-root-privileges) (our main `tines-app` image has been doing this for a while).
- The image can be run in a container [with a read-only filesystem](https://rehansaeed.com/docker-read-file-systems/).
- The image is now signed using [Docker Content Trust](https://docs.docker.com/engine/security/trust/), which allows customers to prove that the images they receive have not been tampered with by a third party. We’ll bring this to the `tines-app` image soon too.

#### Search stories/credentials/resources under folders

- Searching now works within folders

Image digests:

```
tines/tines-app: sha256:4758756e31f697b6d4deb5d16589aaf52a9230a8771548a9cd992dd5c91dc20d
tines/tines-nginx: sha256:765d2e68eb52c5fc8617afe996224bdea3b1aa1bdfdba858ff26de8a060fb2db
```

### September 20, 2021 - 6.2.0

- Tines now supports enhanced SAML security features, such as encrypted responses and signed requests & responses. Documentation can be found here: [https://hub.tines.com/docs/admin/single-sign-on](https://hub.tines.com/docs/admin/single-sign-on)
- Buttons now have focus states to allow for easier keyboard navigation in the app. 

![](https://www.datocms-assets.com/55802/1656589418-focus-f1ed27479e7aa1587c79b4f91acbf97f.gif)

Image digests:

```
tines/tines-app: sha256:8428471bb4d407fbcf321664a894a9b4ebb742772f6c84ea011f664bc01acc85
tines/tines-nginx: sha256:96ff54454b8b1b22d14469596758c9dc40549d1fc54666357c03c6f09997d313
```

### September 6, 2021 - 6.1.0

- No code editor: It's now easier to build stories without writing any code. Add option blocks and configure actions visually, in a few clicks.

![](https://www.datocms-assets.com/55802/1656589441-untitled-8-d8144b5f83c54f2011f4b1f1cf75ddf1.png)

- Event holding for unpublished stories: Events emitted by an webhook action or a scheduled action in an unpublished story will be held until manually emitted. This will enable unpublished stories to be tested safely without inadvertently triggering other actions that are not yet ready for production.

**

Image digests:

```
tines/tines-app: sha256:aca3ff89f89287b10fc5b42926e817e420ab35d459960b81d870f2a759c881e6
tines/tines-nginx: sha256:96ff54454b8b1b22d14469596758c9dc40549d1fc54666357c03c6f09997d313
```

### August 23, 2021 - 6.0.1

> **NOTE:** This is the first 6.x release of Tines - upgrades to this version are only possible from a 5.x release. We recommend self-hosting customers upgrade to 5.3.0 before upgrading to 6.0.0 to ensure that the upgrade process is as smooth as possible.

- When editing an action in the UI, the action can be saved even if its configuration is invalid. This helps prevent losing progress when making changes to an action which can cause it to temporarily have an invalid configuration.
- In self-hosted installations, admin users can now visit `/admin/configuration` to update their limits when updating their pricing plan.

Image digests:

```
tines/tines-app: sha256:c876e421ecc8c35cf8f0e0bf131881031fe43313df5fae73ca43811262d3735e
tines/tines-nginx: sha256:96ff54454b8b1b22d14469596758c9dc40549d1fc54666357c03c6f09997d313
```

### August 9, 2021 - 5.3.0

- Convert cURL commands to Tines Actions
  
  Now you can now paste in cURL commands to the storyboard and you'll get a Tines Action created immediately. This feature should make it much easier to use any documentation that features cURL and kickstart utilizing those API calls within Tines.

**

- Suggested action option examples can now be inserted into the code editor using the + in the editor menu. 

![](https://www.datocms-assets.com/55802/1656589517-options_suggestions-9dc6e8d71034e9787392dabf74bc2b17.gif)

Image digests:

```
tines/tines-app: sha256:5640ce798a266b26cfdb2fd20b0add92373dad90cf3def4a39a17f0a2a0a5727
tines/tines-nginx: sha256:96ff54454b8b1b22d14469596758c9dc40549d1fc54666357c03c6f09997d313
```

### July 26, 2021 - 5.2.0

- New Liquid filters:
  
  - `in_cidr`: Checks if an IP address is in a given range, expressed in CIDR notation - `{{ ip_address | in_cidr: '10.0.0.0/8' }}`.
  - `to_snake_case`: Converts a string of text to snake_case - `{{ 'Liquid filter' | to_snake_case }}` becomes `liquid_filter`.
- After renaming a resource or credential, any references to it which are located in the value of a resource or credential will be automatically updated.
- When renaming a HTTP Request credential, the token location field will automatically update to reflect the new name.
- For self-hosting customers, action templates are now stored in the app's database instead of Algolia. The latest public template data will be included automatically as part of every version upgrade.
  
  - Public templates can also be downloaded automatically from `[template-data.tines.com](http://template-data.tines.com)` by setting `SYNC_TEMPLATES=true` in your instance's environment variables. When enabled, template data will be synchronized every hour.
  - Existing private templates will be downloaded to your instance's database as part of the upgrade process.

Image digests:

```
tines/tines-app: sha256:3306fbf58fb68b24728742d76fa0e20a3e3e80ee18965c572252bd39ff079c8e
tines/tines-nginx: sha256:96ff54454b8b1b22d14469596758c9dc40549d1fc54666357c03c6f09997d313
```

### July 12, 2021 - 5.1.0

- Dark mode is now available. It will follow your system setting by default, or you can specify your preference from the user menu on the top left.

![](https://www.datocms-assets.com/55802/1656589539-dark_mode_ii-74a42370a3d2e40dd051a826871cdc52.mp4)

- Dragging an Action/Annotation to the edge of the storyboard will now scroll the storyboard. This enables you to easily drag items to other areas in the storyboard without having so zoom out before dragging the item.

**

- You can now move a story to new team or folder directly from the diagram.

![](https://www.datocms-assets.com/55802/1656589590-move-a5f59b10df96253d371590d2c4fa0378.gif)

- Updated documentation links to point to our new site: [https://hub.tines.com/](https://hub.tines.com/)

Image digests:

```
tines/tines-app: sha256:9ec9c76ea20012583816b7b462e3370babd2374c330e3f84f6de5590932b08f6
tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f
```

### June 28, 2021 - 5.0.0

> **NOTE:** This is the first 5.x release of Tines - upgrades to this version are only possible from a 4.x release. We recommend self-hosting customers upgrade to 4.3.6 before upgrading to 5.0.0 to ensure that the upgrade process is as smooth as possible.

- Self-hosted installations will send some telemetry data to Tines by default, starting from this version. You can see what data is being sent, and enable/disable the sending of it, by visiting the `/admin/telemetry` path on your tenant. If you want to disable telemetry before this upgrade, you can do so by visiting the `/admin/feature_flags` path and enabling the `telemetry_disabled` feature flag. You can find more information about this here:
- You can now pass initial values for form fields by adding query parameters to the form's URL: ![/release-notes-assets/image_(2) 1.png](/release-notes-assets/image_(2) 1.png)
- The storyboard UI has been refreshed: 

![](https://www.datocms-assets.com/55802/1656589628-screenshot_2021-06-16_at_16-35-25-7fc457595a1347ad64d6c2c9eb06e486.png)

- When you rename an action, you will be prompted to update references to that action in downstream actions: 

![](https://www.datocms-assets.com/55802/1656589657-screenshot_2021-06-17_at_13-14-53-538d1c21946d29f72cb6b190bcccbcbe.png)

- Zooming and scrolling are now smoother and easier:

**

- Webhook actions now support a `Content-Encoding: gzip` header. Encoded requests will be decoded and stored in the event body just like a normal request.
- An API for reading, creating, updating and deleting annotations has been added: [https://docs.tines.com/api_annotations_get_annotation](https://docs.tines.com/api_annotations_get_annotation)

Image digests:

```
tines/tines-app: sha256:24dd64ef6644379047de9478249fb3cd518186192396354ea3b5b64fd6ef0519
tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f
```

### June 14, 2021 - 4.3.0

- Self-hosted notes

##### NOTE

June **30, 2021 - Updated to 4.3.6

-** Added the ability to opt-in to TLS 1.1 support for HTTP request actions.

**Image digests:**

```
tines/tines-app: sha256:7abbd0f9de4dc318fb33dec64d8e2accfad143b79b82deac49c22699b76b001a
tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f
```

##### NOTE

June **23, 2021 - Updated to 4.3.5

-** Added `prepare-database` command which idempotently handles database creation, schema creation, running database migrations, and seeding of initial data.

**Image digests:** `tines/tines-app: sha256:4df18e1b0df0f3f28fef955b84a81a8d9d9e12cdc04796f4404677910a3aded9 tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f`

##### NOTE

June **21, 2021 - Updated to 4.3.4

-** Fixed the app failing to start when the Postgres username does not match the Postgres database name.

**Image digests:** `tines/tines-app: sha256:8c6aadab9d381dd799dc8e5ee113b3533f40d1ec2653e88f6cfef57732846910 tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f`

##### NOTE

June **18, 2021 - Updated to 4.3.3

-** The command to create and seed the database can now run idempotently by running `bundle exec rake db:prepare db:seed`.

**Image digests:** `tines/tines-app: sha256:c7dca8f6b75bc26d31407b02c1dbe4c7d6796efc1f62d46546f8447062377002 tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f`

##### NOTE

June **18, 2021 - Updated to 4.3.2

-** Fixed an issue where stories could not be viewed if templates could not be loaded.

**Image digests:** `tines/tines-app: sha256:b9ad5c41f960afd0ff337664e335ae81a6efec5069e205475511519bcd9154a0 tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f`

##### NOTE

June **18, 2021 - Updated to 4.3.1

-** Fixed an issue where the app would crash on load in some AWS ECS environments.

**Image digests:** `tines/tines-app: sha256:1e728c25beec99bc6a5979e9960f5627a669ca6f58ba63054f590f668af4d356 tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f`

- Annotations can now contain Markdown. 

![](https://www.datocms-assets.com/55802/1656590549-image-1-2-c7cb4a6a0512869c4bfd0cbdc66a41d8.png)

- Folders can now have a custom icon and color.

**

- New loading states on the story page.

![](https://www.datocms-assets.com/55802/1656590593-story-8c3b50ad14446533e4b9330eb5410752.gif)

- You can now pass query parameters to the form in the URL bar. Support for multiple options in the options field will be added in following releases.

![](https://www.datocms-assets.com/55802/1656590612-image-2-3f26356a81401ce53dba7ec6dfa513fe.png)

Image digests:

```
tines/tines-app: sha256:e1ce9808317bc06ec14a9a447476091106a1f2b83e329202600bac44d80ea9fb
tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f
```

### May 31, 2021 - 4.2.0

- Action events list updates in real-time. You no longer have to click `Update` so see recent events after you run an action 

![](https://www.datocms-assets.com/55802/1656590629-reload-2830ecdbba3727e7033da8c9e59534d2.gif)

- New loading states 

![](https://www.datocms-assets.com/55802/1656590642-loadingstates-18ab7e5c307754d717ab691241940cbf.gif)

**Toasts redesign**

– New floating toasts design

![](https://www.datocms-assets.com/55802/1656590679-screen_recording_2021-05-21_at_14-12-50-ee0787d8f071b38fc4b399945bff2d6a.mp4)

– New inline toast design

**

– Undo/Redo directly from a toast

**

Image digests:

```
tines/tines-app: sha256:873b3fb7d65326dfddb715813b3112dc29902d42fedc4a47d2932e9c8a39e40e
tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f
```

### May 17, 2021 - 4.1.0

- All modals have been redesigned: 

![](https://www.datocms-assets.com/55802/1656590749-untitled-9-1b947faa2da8ae2594f394e8be0315bd.png)

- Various updates to the API:
  
  - [Configure Agent monitoring options](https://docs.tines.io/api_actions_update_action)
  - [Configure Sent To Story parameters](https://docs.tines.io/api_stories_update_story)
  - Update [Resource](https://docs.tines.io/api_global_resources_create_text_resource) and [Credential](https://docs.tines.io/api_user_credentials_update_credential) read access
- Action status now updates in real-time

**

Image digests:

```
tines/tines-app: sha256:542b043a54bc006044800f80d526b15ae2b9a8bb4f2da82b9ff427e8a44c64aa
tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f
```

### May 4, 2021 - 4.0.0

> **NOTE:** This is the first 4.x release of Tines - upgrades to this version are only possible from a 3.x release. We recommend self-hosting customers upgrade to 3.3.0 before upgrading to 4.0.0 to ensure that the upgrade process is as smooth as possible.

- Annotations can now be added to a story: 

![](https://www.datocms-assets.com/55802/1656590826-drag-n-drop-299fd896d877c12194410260d0a69301.gif)

- Autocomplete now suggests all actions upstream of the selected action, without the need for them to have emitted any events: 

![](https://www.datocms-assets.com/55802/1656590846-auto-d97cc92b084518a46b8d2c4c1da7224e.gif)

- When you add an email action to the story, its options will now default to your email address: 

![](https://www.datocms-assets.com/55802/1656590861-email_options-02c5bbd128ba6f33029e1d7dc5b357e9.gif)

- When renaming a resource or credential, actions that reference the resource/credential will automatically be updated to reference the new name.
- HTTP Request actions now default to a new `/example-echo` endpoint on your Tines tenant. When the action sends a request to this endpoint, the response will show what data the request contained.
- Added API endpoints for:
  
  - [Managing teams & team members](https://docs.tines.io/api_teams_list_teams)
  - [Managing folders](https://docs.tines.io/api_folders_list_folders)

Image digests:

```
tines/tines-app: sha256:a952afdbe3c9b0efec879ac26c7687aef6505755a537fd017b8e0ea8c9fb61fb
tines/tines-nginx: sha256:25e35413e303b21de90efaa04fd7f0b2d382f62bcca8cc957e20307c0e42f40f
```

# Tines API

## Welcome

The REST API exposes a number of endpoints providing programmatic access to Tines data and functionality.

## Accessing the API

The REST API takes the form of the following URL: `https://<tenant-domain>/api/v1/<api-endpoint>`.

For `<tenant-domain>`, you can copy the domain visible in your web browser when you're navigating the Tines UI. For cloud users this will be similar to either `adjective-noun-1234.tines.io` or `adjective-noun-1234.tines.com`.

## Pagination

Requests that return multiple items, e.g.: events and actions, will be paginated to 20 items by default. You can specify further pages with the `?page` parameter. In addition, you can set a custom page size with the `?per_page` parameter.

```
curl 'https://<tenant-domain>/api/v1/events?page=2&per_page=100'
```

### The 'meta' object

When pagination of items occurs, an object named "meta" will be included in the response body. This object contains the following information:

- `current_page`: A link to the current page of results.
- `previous_page`: A link to the previous page of results (if available).
- `next_page`: A link to the next page of results (if available).
- `next_page_number`: The next page number to facilitate easier navigation through the results.
- `per_page`: The number of items returned per page (defaults to 20, maximum is 500).
- `pages`: The number of pages available.
- `count`: The total number of items returned by the request.

A sample response including the meta object is shown below:

```json
{
    "agents": [...],
    "meta": {
        "current_page": "https://<tenant-domain>/api/v1/agents?page=1&per_page=20",
        "previous_page": null,
        "next_page": "https://<tenant-domain>/api/v1/agents?page=2&per_page=20",
        "next_page_number": 2,
        "per_page": 20,
        "pages": 13,
        "count": 242
    }
}
```

### HTTP Status codes

You can configure your clients to treat the HTTP Responses Codes returned by Tines API as follows:

- Treat 200–299 as success
- Treat 400–499 as client request errors
- Treat 500–599 as Tines server errors

Most 400-level errors and some 500-level errors will include a body in the response containing more information about why the error was returned.

## Authentication and Authorization

All requests to the Tines REST API require authentication.

## Generate API Key

To generate an API key in Tines, use the [Tines connect flow](https://www.tines.com/docs/credentials/connect-flows/tines).

- Navigate to the team that will be using the API and click **Credentials**.
- Click **+ New Credential** and select **Tines** and follow the prompts to connect.

To learn more about connect flows generally, see our [docs](https://www.tines.com/docs/credentials/connect-flows).

## View API Keys

From the Tines settings page, select "API keys" under "Access & security".

![Navigation](https://www.datocms-assets.com/55802/1764675719-api_keys_settings_page.png)

On the API Keys page you can view all of the API keys you have access to or create a new key. While you can create a new key manually from this page, using the [Tines connect flow](https://www.tines.com/docs/credentials/connect-flows/tines) is recommended.

Tines has four types of API keys:

- Personal API keys are linked to your user account, and have access to all items that you do. Operations performed using one of these keys will be recorded as being performed by you. Only you have access to create and delete your personal keys.
- Service API keys are linked to separate service account users, and can be granted access to any tenant permissions such as `AUDIT_LOG_READ` and `TUNNEL_MANAGE` by a tenant owner. Operations performed using one of these keys will be recorded as being performed by the associated service account user.
- Tenant owner API keys are linked to separate service account users, and have full Owner access to the entire tenant. Operations performed using one of these keys will be recorded as being performed by the associated service account user. Tenant Owners have access to create and delete all tenant keys.
- Team API keys are also linked to separate service account users, but they have role-based access to a specific team on the tenant. Operations performed using one of these keys will be recorded as being performed by the associated service account user. Tenant Owners have access to create and delete all team keys.

If you use an underprivileged API key to access a protected resource the key does not have permissions for, you will get a `404: Not Found` response.

## Using an API Key

Each request sent to the REST API must be authenticated using an API key, included in the `X-User-Token` header.

For example:

```bash
curl -X GET https://<tenant-domain>/api/v1/events/ \
  -H 'content-type: application/json' \
  -H 'x-user-token: <<CREDENTIAL.tines_api_key>>'
```

### Bearer/Token Authentication

Our API includes support for Bearer/Token Authentication. To authenticate using this method, simply include your API token in the `Authorization` header of your requests, prefixed with "Bearer" and a single space.

For example:

```bash
curl -X GET https://<tenant-domain>/api/v1/events/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## API rate limits

Most APIs have a limit of 5000 per minute.

The following rate limits apply to specific API endpoints.

| Endpoint      | Requests/Minute |
| ------------- | --------------- |
| actions       | 100             |
| admin/users   | 500             |
| audit_logs    | 1000            |
| records       | 400             |
| tokens (auth) | 2500            |

_Note: These limits are enforced per IP address and API token combination._

## Audit logs

We automatically capture an audit log any time a user changes any piece of data in your Tines tenant.

These audit logs are available to tenant admins via our API, and are typically drained to a log analysis or SIEM tool for further investigation.

## Logged operations

For every operation available in the user interface and API, our system automatically and consistently captures a log.

| Operation                                    | Description                                                                                     |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| ActionDescriptionChange                      | An Action's description was changed                                                             |
| ActionEgressControlRuleChange                | An action egress control rule was changed                                                       |
| ActionEgressControlRuleCreation              | An action egress control rule was created                                                       |
| ActionEgressControlRuleDeletion              | An action egress control rule was deleted                                                       |
| ActionInputAddition                          | An Action input was added                                                                       |
| ActionInputIsCollapsedChange                 | An Action input was collapsed                                                                   |
| ActionInputOptionsUpdate                     | An Action input's options were updated                                                          |
| ActionInputRemoval                           | An Action input was removed                                                                     |
| ActionInputReorder                           | An Action input was reordered                                                                   |
| ActionInputUpdate                            | An Action input was updated                                                                     |
| ActionInputValueUpdate                       | An Action input value was updated                                                               |
| ActionIntegrationCreation                    | An Action integration was created                                                               |
| ActionIntegrationRemoval                     | An Action integration was removed                                                               |
| ActionLogsClearance                          | An Action's logs were cleared                                                                   |
| ActionMemoryClearance                        | An Action's memory was cleared                                                                  |
| ActionMemoryRelease                          | An Action's memory was released                                                                 |
| ActionMetricsChange                          | An Action's time-saved/reporting setting was changed                                            |
| ActionPublicTemplateDisconnect               | An Action was disconnected from a public template                                               |
| ActionRecordTypeChange                       | An Action's record type was changed                                                             |
| ActionRerun                                  | An Action was rerun                                                                             |
| ActionRun                                    | An Action was run                                                                               |
| ActionSizeChange                             | An actions size was changed                                                                     |
| ActionTeamCaseTemplateUpdate                 | An Action's team case template was updated                                                      |
| ActionTest                                   | An Action was tested                                                                            |
| ActionsDisabledChange                        | Multiple Actions had their 'disabled' property changed                                          |
| ActionsEventsDeletion                        | Multiple Actions' events were cleared                                                           |
| ActionsLogsClearance                         | Multiple Actions' logs were cleared                                                             |
| ActionsMonitoringChange                      | Multiple Actions' monitoring settings were changed                                              |
| ActionsNameChange                            | Multiple Actions' names were changed                                                            |
| ActionsOptionsChange                         | Multiple Actions' options were changed                                                          |
| ActionsScheduleChange                        | Multiple Actions' schedules were changed                                                        |
| AiActionConversationCancellation             | An AI action conversation was cancelled                                                         |
| AiActionConversationDeletion                 | An AI action conversation was deleted                                                           |
| AiCustomModelsChange                         | AI custom models were changed                                                                   |
| AiProviderModelChange                        | AI provider model was changed                                                                   |
| AnalyticsCapture                             | An analytics event was captured                                                                 |
| AnnotationContentChange                      | An annotation was edited                                                                        |
| AnnotationSizeChange                         | An annotation was resized                                                                       |
| ApplyPageTemplate                            | A page template was applied                                                                     |
| ArchivedStoryDeleteAll                       | A team's story trash was emptied                                                                |
| ArchivedStoryDeletion                        | An individual story was removed from a team's story trash                                       |
| AuthenticationTokenCreation                  | A new API key was created                                                                       |
| AuthenticationTokenDeletion                  | An API key was deleted                                                                          |
| AuthenticationTokenEdit                      | An API key was edited                                                                           |
| BatchStoryArchive                            | A set of stories were moved to trash                                                            |
| BoardColumnReorder                           | A board column was reordered                                                                    |
| BoardColumnVisibility                        | A board column's visibility was changed                                                         |
| CredentialBulkDestruction                    | Multiple credentials were deleted                                                               |
| CredentialDestruction                        | A credential was deleted                                                                        |
| CredentialIconChange                         | A credential's icon was changed                                                                 |
| CredentialMetadataReplacement                | A credential's metadata was replaced                                                            |
| CredentialMetadataToResourceReplacement      | A credential's metadata was replaced with a resource                                            |
| CredentialMovement                           | A credential was moved to a new team/folder                                                     |
| CredentialOauthTokenRefresh                  | An OAuth credential's token was refreshed                                                       |
| CredentialReplacement                        | A credential was replaced on a story                                                            |
| CredentialSave                               | A credential was created or updated                                                             |
| CredentialTest                               | A credential was tested                                                                         |
| CustomCertificateAuthorityCreation           | A custom certificate authority was created                                                      |
| CustomCertificateAuthorityDeletion           | A custom certificate authority was deleted                                                      |
| CustomCertificateAuthoritySet                | A custom certificate authority was set                                                          |
| CustomTeamRoleCreation                       | A custom team role was created                                                                  |
| CustomTeamRoleDestruction                    | A custom team role was destroyed                                                                |
| CustomTeamRoleUpdate                         | A custom team role was updated                                                                  |
| DashboardChartCreation                       | A dashboard chart was created                                                                   |
| DashboardCreation                            | A dashboard was created                                                                         |
| DashboardDeletion                            | A dashboard was deleted                                                                         |
| DashboardDuplicate                           | A dashboard was duplicated                                                                      |
| DashboardElementCreation                     | A dashboard element was created                                                                 |
| DashboardElementDeletion                     | A dashboard element was deleted                                                                 |
| DashboardListCreation                        | A dashboard list was created                                                                    |
| DashboardListUpdate                          | A dashboard list was updated                                                                    |
| DashboardNoteCreation                        | A dashboard note was created                                                                    |
| DashboardNoteUpdate                          | A dashboard note was updated                                                                    |
| DashboardSubscriptionCreate                  | A dashboard subscription was created                                                            |
| DashboardSubscriptionUpdate                  | A dashboard subscription was updated                                                            |
| DashboardUpdate                              | A dashboard was updated                                                                         |
| DraftCredentialCreation                      | A draft credential was created                                                                  |
| DraftStoryCreation                           | A draft story was created                                                                       |
| DraftStoryDeletion                           | A draft story was deleted                                                                       |
| DraftStoryRebase                             | A draft story was rebased                                                                       |
| DraftStoryReset                              | A draft story was reset                                                                         |
| EventDeletion                                | An event was deleted                                                                            |
| EventLimitSettingCreation                    | An event limit setting was created                                                              |
| EventLimitSettingDeletion                    | An event limit setting was deleted                                                              |
| EventLimitSettingUpdate                      | An event limit setting was updated                                                              |
| EventLimitUserSubscriberCreation             | An event limit user subscriber was created                                                      |
| EventLimitUserSubscriberDeletion             | An event limit user subscriber was deleted                                                      |
| EventLimitWebhookSubscriberCreation          | An event limit webhook subscriber was created                                                   |
| EventLimitWebhookSubscriberDeletion          | An event limit webhook subscriber was deleted                                                   |
| EventReEmission                              | An event was re-emitted                                                                         |
| ExampleStoryImport                           | A story was imported from the story library                                                     |
| FolderCreation                               | A folder was created                                                                            |
| FolderDestruction                            | A folder was destroyed                                                                          |
| FolderUpdate                                 | A folder was updated                                                                            |
| FormFieldAddition                            | A form field was added                                                                          |
| FormFieldDuplication                         | A form field was duplicated                                                                     |
| FormFieldRemoval                             | A form field was removed                                                                        |
| FormFieldReorder                             | A form field's position was reordered                                                           |
| FormFieldUpdate                              | A form field was updated                                                                        |
| FormUpdate                                   | A form was updated                                                                              |
| GlobalResourceCreation                       | A global resource was created                                                                   |
| GlobalResourceDestruction                    | A global resource was deleted                                                                   |
| GlobalResourceEdit                           | A global resource was edited                                                                    |
| GlobalResourceMovement                       | A global resource was moved to a new team/folder                                                |
| GroupExtraction                              | A group was extracted from a set of actions                                                     |
| GroupFlatten                                 | A group was flattened to a set of actions                                                       |
| IntegrationSelection                         | An integration was selected                                                                     |
| IntegrationSelectionUndo                     | An integration selection was undone                                                             |
| IPAccessControlRuleChange                    | An IP access control rule was changed                                                           |
| IPAccessControlRuleCreation                  | An IP access control rule was created                                                           |
| IPAccessControlRuleDeletion                  | An IP access control rule was deleted                                                           |
| JobsQueuedDeletion                           | Currently enqueued jobs were cleared (for a self-hosted tenant)                                 |
| JobsRetryingDeletion                         | Currently retrying jobs were cleared (for a self-hosted tenant)                                 |
| LastEventReEmission                          | The most recent event was re-emitted                                                            |
| LicenseApplication                           | A license was applied                                                                           |
| LicenseValidation                            | A license was validated                                                                         |
| LinkChange                                   | A link between actions was edited                                                               |
| Login                                        | A user logged in                                                                                |
| LoginNoticeChange                            | Login notice was changed                                                                        |
| MergePageElementContainers                   | Page element containers were merged                                                             |
| MoveFormFieldToExistingContainer             | A form field was moved to an existing container                                                 |
| MoveFormFieldToNewContainer                  | A form field was moved to a new container                                                       |
| NotificationBulkDeletion                     | Multiple notifications were deleted                                                             |
| NotificationBulkRead                         | Multiple notifications were marked as read                                                      |
| NotificationDeletion                         | A notification was deleted                                                                      |
| NotificationIsReadChange                     | A notification's read status was changed                                                        |
| NotificationSettingSave                      | A notification setting was saved                                                                |
| OnboardingFormSubmission                     | Onboarding form was submitted                                                                   |
| PageCollectionCreation                       | A page collection was created                                                                   |
| PageCollectionDestruction                    | A page collection was destroyed                                                                 |
| PageCollectionPageMove                       | A page collection page was moved                                                                |
| PageCollectionPageRemove                     | A page was removed from a page collection                                                       |
| PageCollectionPagesAdd                       | Pages were added to a page collection                                                           |
| PageCollectionReorder                        | A page collection was reordered                                                                 |
| PageCollectionUpdate                         | A page collection was updated                                                                   |
| PageDisplayLogoSet                           | A page display logo was set                                                                     |
| PageElementButtonGroupToggle                 | A page element button group was toggled                                                         |
| PageElementClearance                         | Page elements were cleared                                                                      |
| PageElementConditionRuleCreation             | A page element condition rule was created                                                       |
| PageElementConditionRuleDestruction          | A page element condition rule was destroyed                                                     |
| PageElementConditionRuleUpdate               | A page element condition rule was updated                                                       |
| PageElementConditionUpdate                   | A page element condition was updated                                                            |
| PageElementContainerAddition                 | A page element container was added                                                              |
| PageElementContainerDuplication              | A page element container was duplicated                                                         |
| PageElementContainerRemoval                  | A page element container was removed                                                            |
| PageElementContainerReplaceElements          | Page element container elements were replaced                                                   |
| PageElementContainerUpdate                   | A page element container was updated                                                            |
| PageEmojiSet                                 | A page emoji was set                                                                            |
| PageLogoCreation                             | A page logo was created                                                                         |
| PageLogoSet                                  | A page logo was set                                                                             |
| PageSsoGroupsUpdate                          | Page SSO groups were updated                                                                    |
| PageThemeApplication                         | A page theme was applied                                                                        |
| PageThemeCreation                            | A page theme was created                                                                        |
| PageThemeDestruction                         | A page theme was destroyed                                                                      |
| PreviewPageTemplate                          | A page template was previewed                                                                   |
| RecordBulkDeletion                           | Multiple records were deleted                                                                   |
| RecordElementExportRequest                   | A record element export was requested                                                           |
| RecordReportClone                            | A record report was cloned                                                                      |
| RecordReportCreation                         | A Record report was created                                                                     |
| RecordReportDeletion                         | A Record report was deleted                                                                     |
| RecordReportElementColumnReorder             | A Record report element column was reordered                                                    |
| RecordReportElementCreation                  | A Record report element was created                                                             |
| RecordReportElementDestruction               | A Record report element was deleted                                                             |
| RecordReportElementUpdate                    | A Record report element was edited                                                              |
| RecordReportFiltersUpdate                    | A record report's filters were updated                                                          |
| RecordReportUpdate                           | A Record report was edited                                                                      |
| RecordResultSetCreate                        | A record result set was created                                                                 |
| RecordResultSetDestruction                   | A record result set was destroyed                                                               |
| RecordResultSetUpdate                        | A Record result set was updated                                                                 |
| RecordTypeCreation                           | A Record type was created                                                                       |
| RecordTypeDestruction                        | A Record type was deleted                                                                       |
| RecordTypeUpdate                             | A Record type was updated                                                                       |
| RecordWriterCreation                         | A Record writer was created                                                                     |
| RecordWriterDestruction                      | A Record writer was deleted                                                                     |
| RecordWriterUpdate                           | A Record writer was edited                                                                      |
| ResourceIconChange                           | A resource's icon was changed                                                                   |
| ResourceLockedUpdate                         | A resource's locked status was updated                                                          |
| ResourceReplacement                          | A global resource was replaced on a story                                                       |
| ResourceToCredentialMetadataReplacement      | A resource was replaced with credential metadata                                                |
| RunActionWithReceivedEvent                   | An action was run with a received event                                                         |
| SavedStoryRunModification                    | A saved story run was modified                                                                  |
| SCIM/2.0/User/{operation}                    | A SCIM 2.0 operation (create, replace, update or destroy) on a User                             |
| SenderEmailAddressCreation                   | A sender email address was created                                                              |
| SenderEmailAddressDeletion                   | A sender email address was deleted                                                              |
| SharedObjectLinkCreation                     | A shared object link was created                                                                |
| SharedObjectLinkUpdate                       | A shared object link was updated                                                                |
| SharedObjectLinkViewSave                     | A shared object link view was saved                                                             |
| SmtpConfigurationTest                        | An SMTP configuration was tested                                                                |
| SsoConfigurationDefaultSet                   | The default SSO configuration was set to Tines-powered                                          |
| SsoConfigurationJitConfigurationSet          | The SSO JIT configuration was set                                                               |
| SsoConfigurationOidcSet                      | The default SSO configuration was set to OIDC                                                   |
| SsoConfigurationPageGroupAttributeNameUpdate | The SSO page group attribute name was updated                                                   |
| SsoConfigurationSamlSet                      | The default SSO configuration was set to SAML                                                   |
| StoryActionsAutoAlign                        | A set of actions were auto aligned                                                              |
| StoryActionsAutoLayout                       | A set of actions were auto laid-out                                                             |
| StoryActionsPositioning                      | A set of actions and annotations were repositioned                                              |
| StoryAPIChange                               | Enable webhook API responses on a story                                                         |
| StoryArchive                                 | A story was moved to trash                                                                      |
| StoryChangeRequestApproval                   | A story change request was approved                                                             |
| StoryChangeRequestCancellation               | A story change request was cancelled                                                            |
| StoryChangeRequestCreation                   | A story change request was created                                                              |
| StoryChangeRequestPromotion                  | A story change request was promoted                                                             |
| StoryContainerFavoritedChange                | A story container's favorited status was changed                                                |
| StoryContainerOwnerDelete                    | A story container owner was deleted                                                             |
| StoryContainerOwnerUpdate                    | A story container owner was updated                                                             |
| StoryContainerSubscriberCreation             | A story container subscriber was created                                                        |
| StoryContainerSubscriberDeletion             | A story container subscriber was deleted                                                        |
| StoryCreation                                | A story was created                                                                             |
| StoryDataDeletion                            | A story's data was deleted                                                                      |
| StoryDescriptionChange                       | A story's description was changed                                                               |
| StoryDisabledChange                          | A story was disabled or enabled                                                                 |
| StoryEventsDeletion                          | All of a story's events were deleted                                                            |
| StoryIconChange                              | A story's icon was changed                                                                      |
| StoryImport                                  | A story was imported                                                                            |
| StoryIntegrationFieldsUpdate                 | A story's integration fields were updated                                                       |
| StoryItemsCreation                           | A set of actions/annotations were created                                                       |
| StoryItemsDestruction                        | A set of actions/annotations were destroyed                                                     |
| StoryItemsMovement                           | A set of actions/annotations were moved                                                         |
| StoryJobsClearance                           | A story's scheduled jobs were cleared                                                           |
| StoryKeepEventsForChange                     | A story's event retention settings were changed                                                 |
| StoryLegacyUnpublish                         | A legacy story was unpublished                                                                  |
| StoryLibraryMetadataChange                   | A story library's metadata was changed                                                          |
| StoryLLMProductInstructionsChange            | A story's LLM product instructions were changed                                                 |
| StoryLockedChange                            | A story was locked or unlocked                                                                  |
| StoryMetricsChange                           | A story's metrics were changed                                                                  |
| StoryMonitoringChange                        | A story's monitoring settings were changed                                                      |
| StoryMovement                                | A story was moved                                                                               |
| StoryNameChange                              | A story's name was changed                                                                      |
| StoryOnboardingFinish                        | Story onboarding was finished                                                                   |
| StoryOnboardingStart                         | Story onboarding was started                                                                    |
| StoryOnboardingUnpause                       | Story onboarding was unpaused                                                                   |
| StoryPriorityChange                          | A story's priority was changed                                                                  |
| StoryRecipientAddition                       | A monitoring recipient was added for a story                                                    |
| StoryRecipientRemoval                        | A monitoring recipient was removed for a story                                                  |
| StoryRecipientUpdate                         | A monitoring recipient was changed for a story                                                  |
| StoryReportingStatusChange                   | Reporting was enabled/disabled for a story                                                      |
| StoryRequireStoryOwnerApprovalUpdate         | Story owner approval requirement was updated                                                    |
| StoryRestore                                 | A story was restored from the trash                                                             |
| StoryRunMockPayloadMappingCreation           | A story run mock payload mapping was created                                                    |
| StoryRunMockPayloadMappingDeletion           | A story run mock payload mapping was deleted                                                    |
| StoryRunSaveSettingModification              | A story run save setting was modified                                                           |
| StorySendToStoryChange                       | A story's send-to-story settings were edited                                                    |
| StoryTagsUpdate                              | A story's tags were updated                                                                     |
| StoryVersionCreate                           | A story version was created                                                                     |
| StoryVersionDeletion                         | A story version was deleted                                                                     |
| StoryVersionImport                           | A story version was imported                                                                    |
| StoryVersionNameChange                       | A story's version was renamed                                                                   |
| StoryViewSave                                | A story view was saved                                                                          |
| SyncDestinationDeletion                      | A sync destination was deleted                                                                  |
| SyncDestinationManualSync                    | A sync destination was manually synced                                                          |
| SyncDestinationSave                          | A sync destination was saved                                                                    |
| SyncedStoryDetach                            | A synced story was detached                                                                     |
| TagChange                                    | A tag was changed                                                                               |
| TagCreation                                  | A tag was created                                                                               |
| TagDeletion                                  | A tag was deleted                                                                               |
| TeamAllocationChange                         | A team's allocation was changed                                                                 |
| TeamCaseAddCommentReaction                   | A reaction was added to a case comment                                                          |
| TeamCaseBlockCreation                        | A team case block was created                                                                   |
| TeamCaseBlockDeletion                        | A team case block was deleted                                                                   |
| TeamCaseBlockElementUpdate                   | A team case block element was updated                                                           |
| TeamCaseBlockUpdate                          | A team case block was updated                                                                   |
| TeamCaseBoardMovement                        | A team case board movement occurred                                                             |
| TeamCaseBulkCommentCreation                  | Comments were bulk created on team cases                                                        |
| TeamCaseBulkDeletion                         | Team cases were bulk deleted                                                                    |
| TeamCaseBulkLinkCase                         | Team cases were bulk linked                                                                     |
| TeamCaseBulkUpdate                           | Team cases were bulk updated                                                                    |
| TeamCaseButtonCreation                       | An action (button) was added to a case                                                          |
| TeamCaseButtonDeletion                       | A team case button was deleted                                                                  |
| TeamCaseButtonsBulkUpdate                    | Team case buttons were bulk updated                                                             |
| TeamCaseButtonUpdate                         | A team case button was updated                                                                  |
| TeamCaseButtonWebhookTrigger                 | A team case button webhook was triggered                                                        |
| TeamCaseCommentCreation                      | A comment was added to a case                                                                   |
| TeamCaseCommentDeletion                      | A team case comment was deleted                                                                 |
| TeamCaseCommentUpdate                        | A comment was updated on a case                                                                 |
| TeamCaseCreation                             | A case was created                                                                              |
| TeamCaseDeletion                             | A team case was deleted                                                                         |
| TeamCaseDescriptionUpdate                    | A team case's description was updated                                                           |
| TeamCaseFileAttach                           | A file or comment and file was attached to a case                                               |
| TeamCaseFileBlockCreation                    | A team case file block was created                                                              |
| TeamCaseFileBlockDeletion                    | A team case file block was deleted                                                              |
| TeamCaseFileBlockElementCreation             | A team case file block element was created                                                      |
| TeamCaseFileDeletion                         | A team case file was deleted                                                                    |
| TeamCaseInputCreation                        | A team case input was created                                                                   |
| TeamCaseInputDeletion                        | A team case input was deleted                                                                   |
| TeamCaseInputUpdate                          | A team case input was updated                                                                   |
| TeamCaseInputValueDeletion                   | A team case input value was deleted                                                             |
| TeamCaseInputValueUpsert                     | A team case input value was upserted                                                            |
| TeamCaseLinkCase                             | A team case was linked to another case                                                          |
| TeamCaseMetadataCreation                     | Team case metadata was created                                                                  |
| TeamCaseMetadataUpdate                       | A team case's metadata was updated                                                              |
| TeamCaseNoteBlockCreation                    | A team case note block was created                                                              |
| TeamCaseNoteDeletion                         | A team case note was deleted                                                                    |
| TeamCaseNoteUpdate                           | A team case note was updated                                                                    |
| TeamCaseRecordFieldsUpdate                   | Team case record fields were updated                                                            |
| TeamCaseRecordResultSetBulkDestruction       | Team case record result sets were bulk destroyed                                                |
| TeamCaseRecordResultSetCreation              | A team case record result set was created                                                       |
| TeamCaseRecordResultSetDestruction           | A team case record result set was destroyed                                                     |
| TeamCaseRemoveCommentReaction                | A reaction was removed from a comment on a case                                                 |
| TeamCasesExportCreation                      | A team cases export was created                                                                 |
| TeamCaseSlaCreation                          | A team case SLA was created                                                                     |
| TeamCaseSlaDeletion                          | A team case SLA was deleted                                                                     |
| TeamCaseSlaUpdate                            | A team case SLA was updated                                                                     |
| TeamCasesSavedViewCreation                   | A saved view was created for a teams cases                                                      |
| TeamCasesSavedViewDestruction                | A saved view was deleted from a teams cases                                                     |
| TeamCasesSavedViewUpdate                     | A team cases saved view was updated                                                             |
| TeamCaseStatusCreation                       | A team case status was created                                                                  |
| TeamCaseStatusDeletion                       | A team case status was deleted                                                                  |
| TeamCaseStatusUpdate                         | A team case status was updated                                                                  |
| TeamCaseSubscriberBulkCreation               | Team case subscribers were bulk created                                                         |
| TeamCaseSubscriberCreation                   | A subscriber was added to a case                                                                |
| TeamCaseSubscriberDeletion                   | A subscriber was removed from a case                                                            |
| TeamCaseTagsUpdate                           | A tag was added to or removed from a case                                                       |
| TeamCaseTemplateCreation                     | A team case template was created                                                                |
| TeamCaseTemplateDeletion                     | A team case template was deleted                                                                |
| TeamCaseTemplateUpdate                       | A team case template was updated                                                                |
| TeamCaseUnlinkCase                           | A team case was unlinked                                                                        |
| TeamCaseUpdate                               | A case was assigned, renamed, completed or its description updated                              |
| TeamCaseWebhookUpdate                        | A case notification webhook was set or updated for the team                                     |
| TeamCreation                                 | A team was created                                                                              |
| TeamDestruction                              | A team was destroyed                                                                            |
| TeamMemberRemoval                            | A team member was removed                                                                       |
| TeamMembersAddition                          | A team member was added                                                                         |
| TeamNotificationSettingsUpdate               | Team notification settings were updated                                                         |
| TeamSettingUpdate                            | A team setting was updated                                                                      |
| TeamStaticExternalIdDestruction              | A team static external ID was destroyed                                                         |
| TeamTunnelMembershipUpdate                   | A team tunnel membership was updated                                                            |
| TeamUpdate                                   | A team was updated                                                                              |
| TemplateActionTagUpdate                      | A template action tag was updated                                                               |
| TemplateCreation                             | A template was created                                                                          |
| TemplateDestruction                          | A template was deleted                                                                          |
| TemplateDuplication                          | A template was duplicated                                                                       |
| TemplateTestRun                              | A template test was run                                                                         |
| TemplateUpdate                               | A template was updated                                                                          |
| TenantAiEnabledToggle                        | Tenant AI was enabled/disabled                                                                  |
| TenantAiProviderChange                       | A tenant AI provider was changed                                                                |
| TenantAiProviderDeletion                     | A tenant AI provider was deleted                                                                |
| TenantAiProviderTest                         | Tenant AI provider was tested                                                                   |
| TenantChangeControlSettingsUpdate            | Tenant change control settings were updated                                                     |
| TenantConfigChange                           | A tenant's config was changed                                                                   |
| TenantFeatureFlagToggle                      | A feature flag was enabled/disabled                                                             |
| TenantLimitsChange                           | Tenant limits were changed                                                                      |
| TenantPlanUpgradeRequest                     | A plan upgrade was requested                                                                    |
| TenantSessionTimeoutChange                   | Tenant session timeout was changed                                                              |
| TenantSettingsChange                         | Tenant settings were changed                                                                    |
| TestStoryCreation                            | Change control was enabled for a story                                                          |
| TestStoryDeletion                            | Change control was disabled for a story                                                         |
| ToolLinkCreation                             | A tool link was created                                                                         |
| ToolLinkDeletion                             | A tool link was deleted                                                                         |
| ToolLinkGroupCreation                        | A tool link group was created                                                                   |
| ToolLinkStsCreation                          | A tool link STS was created                                                                     |
| ToolLinkTemplatesCreation                    | Tool link templates were created                                                                |
| TunnelDestruction                            | A tunnel was destroyed                                                                          |
| TunnelUpdate                                 | A tunnel was updated                                                                            |
| UpdatePageFromTemplate                       | A page was updated from a template                                                              |
| UploadImage                                  | An image was uploaded                                                                           |
| UserCredentialDestruction                    | A user credential was destroyed                                                                 |
| UserCredentialHttpRequestRun                 | A user credential HTTP request was run                                                          |
| UserCredentialMovement                       | A user credential was moved                                                                     |
| UserCredentialSave                           | A user credential was saved                                                                     |
| UserDeletion                                 | A user was deleted                                                                              |
| UserEdit                                     | A user was edited                                                                               |
| UserFavoriteIntegrationProductChange         | A user's favorite integration product was changed                                               |
| UserFavoriteTemplateChange                   | A user's favorite template was changed                                                          |
| UserImpersonationDisabled                    | User impersonation was disabled                                                                 |
| UserImpersonationEnabled                     | User impersonation was enabled                                                                  |
| UserRecoveryCodesDeletion                    | User recovery codes were deleted                                                                |
| UserRecoveryCodesGeneration                  | User recovery codes were generated                                                              |
| UserReinvitation                             | A user's invitation was resent                                                                  |
| UsersInvitation                              | A user was invited                                                                              |
| UserSessionExpiryChange                      | User session expiry was changed                                                                 |
| UserTeamRoleChange                           | A user's team role was changed                                                                  |
| WorkbenchConfigurationCreation               | A Workbench preset was created                                                                  |
| WorkbenchConfigurationDeletion               | A Workbench preset was permanently deleted                                                      |
| WorkbenchConfigurationDuplication            | A Workbench configuration was duplicated                                                        |
| WorkbenchConfigurationUpdate                 | A Workbench preset was updated                                                                  |
| WorkbenchConfiguredProductUpdate             | A user enabled/disabled a product or changed its credential in Workbench                        |
| WorkbenchConfiguredStoryUpdate               | A story was enabled/disabled in Workbench                                                       |
| WorkbenchConversationDeletion                | A Workbench conversation was permanently deleted                                                |
| WorkbenchConversationFork                    | A new conversation was created, with context up to a fixed point from the original conversation |
| WorkbenchConversationRename                  | A Workbench conversation was renamed                                                            |
| WorkbenchConversationStart                   | A new conversation was started in Workbench                                                     |
| WorkbenchConversationSubmitForEvaluation     | A Workbench conversation was submitted for evaluation                                           |
| WorkbenchConversationToggleFavorite          | Favorite was enabled/disabled for a conversation                                                |
| WorkbenchCustomSlackIntegrationCreation      | A Workbench integration was created for Slack                                                   |
| WorkbenchDraftStoryCreation                  | A draft story was created from a Workbench conversation                                         |
| WorkbenchEnableProductTemplates              | A user enabled/disabled templates for a product in Workbench                                    |
| WorkbenchInstructionChange                   | Custom instructions for a user were updated                                                     |
| WorkbenchIntegrationCreation                 | A Workbench integration was created                                                             |
| WorkbenchPresetCreation                      | A Workbench preset was created                                                                  |
| WorkbenchPresetUpdate                        | A Workbench preset updated                                                                      |
| WorkbenchToolUseCancel                       | User canceled the use of a tool                                                                 |
| WorkbenchToolUseConfirm                      | User confirmed the use of a tool                                                                |
| WorkbenchUserMessageSend                     | User sent a message to Workbench                                                                |
| WorkbenchUserMessageStop                     | User stopped a reply from Workbench                                                             |

## Log format

Each log contains enriched user information, as well as metadata related to the operation carried out. Extremely sensitive information, for example credential values, are never included.

```json
{
  "created_at": "2022-10-04T19:03:18Z",
  "id": 123456,
  "inputs": {
    "inputs": {
      "teamId": 1772
    }
  },
  "request_ip": "11.22.33.44",
  "request_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
  "tenant_id": 9,
  "updated_at": "2022-10-04T19:03:18Z",
  "user_email": "user@example.com",
  "user_id": 9,
  "user_name": "Example User",
  "operation_name": "StoryCreation"
}
```

### List

## Description

Returns a list of audit logs gathered from the Tines tenant.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                                                                                                                                            |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| before          | **Optional** Only include logs created before this timestamp                                                                                                                                                           |
| after           | **Optional** Only include logs created after this timestamp                                                                                                                                                            |
| user_id         | **Optional** Only include logs from a matching user. Use the format `user_id[]=` to select multiple users, i.e. `?user_id[]=1&user_id[]=2`.                                                                            |
| operation_name  | **Optional** Only include logs with a specific operation name. Use the format `operation_name[]=` to select multiple operation names, i.e. `?operation_name[]=StoryItemsMovement&operation_name[]=StoryItemsCreation`. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                                                                                                                                              |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                                                                                                    |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/audit_logs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful response will return a list of logs for every user operation executed on the database along with the associated inputs and the id of the user who triggered the operation.

### Field description

| Parameter          | Description                                                                  |
| ------------------ | ---------------------------------------------------------------------------- |
| created_at         | ISO 8601 Timestamp representing creation date and time of log operation.     |
| operation_name     | The name of the operation.                                                   |
| id                 | Operation ID                                                                 |
| inputs             | JSON Inputs passed to the operation.                                         |
| outputs            | JSON Outputs generated by the operation.                                     |
| request_ip         | The IP Address the operation was triggered from                              |
| request_user_agent | The user agent that the operation was triggered with                         |
| tenant_id          | The ID of the tenant the operation was triggered on.                         |
| updated_at         | ISO 8601 Timestamp representing last updated date and time of log operation. |
| user_email         | The email of the user who triggered the operation.                           |
| user_id            | The ID of the user who triggered the operation.                              |
| user_name          | The name of the user who triggered the operation.                            |
| story_id           | ID of the story that the operation was run on (null if there is no story).   |

### Sample response

```json
{
  "audit_logs": [
    {
      "created_at": "2022-06-24T08:35:21Z",
      "operation_name": "StoryItemsMovement",
      "id": 19990840,
      "inputs": {
        "storyId": 3480,
        "inputs": {
          "actionIds": [21246],
          "diagramNoteIds": [],
          "delta": {
            "x": 61,
            "y": 0
          }
        }
      },
      "outputs": {},
      "request_ip": "127.0.0.1",
      "request_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36",
      "tenant_id": 1,
      "updated_at": "2022-06-24T08:35:21Z",
      "user_email": "name@example.com",
      "user_id": 622,
      "user_name": "Name Person",
      "story_id": 3480
    }
  ],
  "meta": {
    "current_page": "https://hq.tines.io/api/v1/audit_logs?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

## Credentials

### Create: AWS type

## Description

Use a HTTP POST request to create a [AWS](/docs/credentials/aws) credential.

## Request

HTTP Method: **POST**

| Parameter                                  | Description                                                                                                                                                                                                                               |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                       | Name of the credential.                                                                                                                                                                                                                   |
| mode                                       | Describes the type of credential (`AWS`)                                                                                                                                                                                                  |
| team_id                                    | ID of Tines Team where the credential will be located.                                                                                                                                                                                    |
| aws_authentication_type                    | The authentication method with AWS, key-based-access or role-based-access(`KEY`, `ROLE`, `INSTANCE_PROFILE`)                                                                                                                              |
| aws_access_key                             | The `access key` from your [AWS Security Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)                                                                                                   |
| aws_secret_key                             | The `access secret` from your [AWS Security Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)                                                                                                |
| aws_assumed_role_arn                       | **Required for role-based-access** The ARN of the role you wish to assume, e.g.: `arn:aws:iam::123456789012:role/write-access-role`                                                                                                       |
| use_static_external_id                     | **Optional** Boolean to indicate whether the credential should use a [team-scoped static external ID](/docs/credentials/aws/#static-external-ids)                                                                                         |
| folder_id                                  | **Optional** ID of folder to which the credential will be located                                                                                                                                                                         |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                   |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                      |
| description                                | **Optional** Description of the credential. default: `""` (empty string)                                                                                                                                                                  |
| metadata                                   | **Optional** Key/value metadata relevant to the credential that can be referenced via the INFO path.                                                                                                                                      |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                |
| live_credential_id                         | **Optional** ID of the live credential                                                                                                                                                                                                    |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "aws credential",
        "mode": "AWS",
        "team_id": 2,
        "aws_authentication_type": "ROLE",
        "aws_access_key": "v_access_key",
        "aws_secret_key": "v_secret_key",
        "aws_assumed_role_arn": "v_role_arn"
    }'
```

## Response

A successful request will return a JSON object describing the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| aws_assumed_role_external_id               | External ID generated for the remote role in your AWS account.                                                                                                                                                               |
| use_static_external_id                     | A boolean value stating if the credential uses a team scoped static external ID.                                                                                                                                             |
| aws_authentication_type                    | The authentication method with AWS, key-based-access or role-based-access(`KEY`, `ROLE`, `INSTANCE_PROFILE`)                                                                                                                 |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata relevant to the credential                                                                                                                                                                                |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "tines_api_credential",
  "mode": "AWS",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "aws_assumed_role_external_id": "1e52dbcf-3621-4969-9bf6-3fd2699db84b",
  "use_static_external_id": false,
  "aws_authentication_type": "ROLE",
  "allowed_hosts": [],
  "metadata": {},
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### Create: HTTP Request type

## Description

Use a HTTP POST request to create a [HTTP Request](/docs/credentials/http-request) credential.

## Request

HTTP Method: **POST**

| Parameter                                  | Description                                                                                                                                                                                                                               |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                       | Name of the credential.                                                                                                                                                                                                                   |
| mode                                       | Describes the type of credential (`HTTP_REQUEST_AGENT`)                                                                                                                                                                                   |
| team_id                                    | ID of Tines Team where the credential will be located.                                                                                                                                                                                    |
| http_request_options                       | JSON object representing the Agents::HTTPRequestAgent Options                                                                                                                                                                             |
| http_request_location_of_token             | Location of token from response                                                                                                                                                                                                           |
| http_request_secret                        | **Optional** Secret value to save in an http request. This can be referenced as `secret` in the HTTP request options builder.                                                                                                             |
| http_request_ttl                           | **Optional** Time to live (TTL) in seconds. Not sending this value during an update/PUT operation will update it to null.                                                                                                                 |
| folder_id                                  | **Optional** ID of folder to which the credential will be located                                                                                                                                                                         |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                   |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                      |
| description                                | **Optional** Description of the credential. default: `""` (empty string).                                                                                                                                                                 |
| metadata                                   | **Optional** Key/value metadata relevant to the credential that can be referenced via the INFO path.                                                                                                                                      |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                |
| live_credential_id                         | **Optional** ID of the live credential                                                                                                                                                                                                    |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample request

The `<<secret>>` maps to the value provided to the `http_request_secret` key as described above.

<!-- cspell:disable -->

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
      "name": "http credential",
      "mode": "HTTP_REQUEST_AGENT",
      "team_id": 2,
      "http_request_options": {
        "url": "http://www.example.com",
        "content_type": "application_json",
        "method": "post",
        "payload": {
          "key": "value",
          "secret": "I can reference the \\<<secret>> as needed in the payload."
        },
        "headers": {}
      },
      "http_request_location_of_token": "\\=credential_name.body.token",
      "http_request_secret": "secret_value",
      "allowed_hosts": ["exampledomain.com", "seconddomain.com"]
    }'
```

<!-- cspell:enable -->

## Response

A successful request will return a JSON object describing the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata relevant to the credential                                                                                                                                                                                |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "tines_api_credential",
  "mode": "HTTP_REQUEST_AGENT",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "allowed_hosts": [],
  "metadata": {},
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### Create: JWT type

## Description

Use a HTTP POST request to create a [JWT tokens](/docs/credentials/jwt) credential.

## Request

HTTP Method: **POST**

| Parameter                                  | Description                                                                                                                                                                                                                               |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                       | Name of the credential.                                                                                                                                                                                                                   |
| mode                                       | Describes the type of credential (`JWT`)                                                                                                                                                                                                  |
| jwt_algorithm                              | The algorithm to be used when computing the JWT                                                                                                                                                                                           |
| jwt_payload                                | The payload to be included in the JWT                                                                                                                                                                                                     |
| jwt_auto_generate_time_claims              | Auto generate ‘iat’ & ‘exp’ claims                                                                                                                                                                                                        |
| jwt_private_key                            | The private key to be used to sign the JWT                                                                                                                                                                                                |
| team_id                                    | ID of Tines Team where the credential will be located.                                                                                                                                                                                    |
| folder_id                                  | **Optional** ID of folder to which the credential will be located                                                                                                                                                                         |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                   |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                      |
| description                                | **Optional** Description of the credential. default: `""` (empty string).                                                                                                                                                                 |
| metadata                                   | **Optional** Key/value metadata relevant to the credential that can be referenced via the INFO path.                                                                                                                                      |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                |
| live_credential_id                         | **Optional** ID of the live credential                                                                                                                                                                                                    |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "jwt credential",
        "mode": "JWT",
        "team_id": 2,
        "jwt_algorithm": "RS256",
        "jwt_payload": {
          "iss": "",
          "sub": "",
          "scope": "",
          "aud": ""
        },
        "jwt_auto_generate_time_claims": true,
        "jwt_private_key": "<private-key>"
    }'
```

## Response

A successful request will return a JSON object describing the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata relevant to the credential                                                                                                                                                                                |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "tines_api_credential",
  "mode": "JWT",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "allowed_hosts": [],
  "metadata": {},
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### Create: MTLS type

## Description

Use a HTTP POST request to create a [Mutual TLS](/docs/credentials/mtls) credential.

## Request

HTTP Method: **POST**

| Parameter                                  | Description                                                                                                                                                                                                                               |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                       | Name of the credential.                                                                                                                                                                                                                   |
| mode                                       | Describes the type of credential (`MTLS`)                                                                                                                                                                                                 |
| team_id                                    | ID of Tines Team where the credential will be located.                                                                                                                                                                                    |
| mtls_client_certificate                    | The certificate file issued by the CA for this client                                                                                                                                                                                     |
| mtls_client_private_key                    | The private key file for the client certificate                                                                                                                                                                                           |
| mtls_root_certificate                      | The root certificate file for the certificate authority (CA) responsible for signatures                                                                                                                                                   |
| folder_id                                  | **Optional** ID of folder to which the credential will be located                                                                                                                                                                         |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                   |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                      |
| description                                | **Optional** Description of the credential. default: `""` (empty string).                                                                                                                                                                 |
| metadata                                   | **Optional** Key/value metadata relevant to the credential that can be referenced via the INFO path.                                                                                                                                      |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                |
| live_credential_id                         | **Optional** ID of the live credential                                                                                                                                                                                                    |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "mtls credential",
        "mode": "MTLS",
        "team_id": 2,
        "mtls_client_certificate": "<mtls_client_certificate_text>",
        "mtls_client_private_key": "<mtls_client_private_key_text>",
        "mtls_root_certificate": "<mtls_root_certificate_text>"
    }'
```

## Response

A successful request will return a JSON object describing the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata relevant to the credential                                                                                                                                                                                |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "tines_api_credential",
  "mode": "MTLS",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "allowed_hosts": [],
  "metadata": {},
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### Create: Multi Request type

## Description

Use a HTTP POST request to create a [Multi Request](/docs/credentials/multi-request) credential.

## Request

HTTP Method: **POST**

| Parameter                                  | Description                                                                                                                                                                                                                                                                                                                                                                                                                 |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                       | Name of the credential.                                                                                                                                                                                                                                                                                                                                                                                                     |
| mode                                       | Describes the type of credential (`MULTI_REQUEST`)                                                                                                                                                                                                                                                                                                                                                                          |
| team_id                                    | ID of Tines Team where the credential will be located.                                                                                                                                                                                                                                                                                                                                                                      |
| http_request_location_of_token             | Location of token from response                                                                                                                                                                                                                                                                                                                                                                                             |
| credential_requests                        | Details of the HTTP requests that are made to fetch the credential value. This should be an array of two objects, each with a required `options` key containing the JSON object representing the Agents::HTTPRequestAgent and an `http_request_secret` key, used to pass secrets to the options. The order of the objects in the array passed to `credential_requests` determines the order in which the requests will run. |
| http_request_ttl                           | **Optional** Time to live (TTL) in seconds. Not sending this value during an update/PUT operation will update it to null.                                                                                                                                                                                                                                                                                                   |
| folder_id                                  | **Optional** ID of folder to which the credential will be located                                                                                                                                                                                                                                                                                                                                                           |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                                                                                                                                                                                                     |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                                                                                                                                                                                                        |
| description                                | **Optional** Description of the credential. default: `""` (empty string).                                                                                                                                                                                                                                                                                                                                                   |
| metadata                                   | **Optional** Key/value metadata relevant to the credential that can be referenced via the INFO path.                                                                                                                                                                                                                                                                                                                        |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                                                                                                                                                                                                  |
| live_credential_id                         | **Optional** ID of the live credential                                                                                                                                                                                                                                                                                                                                                                                      |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                                                                                                                                                                                                    |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders)                                                                                                                                                                                   |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                                                                                                                                                                                                        |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "name": "multi request credential",
  "mode": "MULTI_REQUEST",
  "team_id": "=META.team.id",
  "http_request_location_of_token": "\\=credential_name.body.token",
  "credential_requests": [
    {
      "options": {
        "url": "http://www.example.com",
        "content_type": "application_json",
        "method": "post",
        "payload": {
          "key": "value",
          "secret": "I can reference the \\<<secret>> as needed in the payload."
        },
        "headers": {}
      },
      "http_request_secret": "secret_value"
    },
    {
      "options": {
        "url": "http://www.example.com",
        "content_type": "application_json",
        "method": "post",
        "payload": {
          "key": "value",
          "secret": "I can reference the \\<<PREVIOUS_STEP>> or the \\<<secret>> as needed in the payload."
        },
        "headers": {}
      },
      "http_request_secret": "secret_value"
    }
  ]
}'
```

## Response

A successful request will return a JSON object describing the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata relevant to the credential                                                                                                                                                                                |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "tines_api_credential",
  "mode": "MULTI_REQUEST",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "allowed_hosts": [],
  "metadata": {},
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### Create: OAuth type

## Description

Use a HTTP POST request to create a [OAuth2.0](/docs/credentials/oauth) credential.

## Request

HTTP Method: **POST**

| Parameter                                  | Description                                                                                                                                                                                                                               |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                       | Name of the credential.                                                                                                                                                                                                                   |
| mode                                       | Describes the type of credential (`OAUTH`).                                                                                                                                                                                               |
| oauth_url                                  | Your app oauth url. Required for grant type `authorization_code`.                                                                                                                                                                         |
| oauth_token_url                            | Your app oauth token url                                                                                                                                                                                                                  |
| oauth_client_id                            | The client ID for your app                                                                                                                                                                                                                |
| oauth_client_secret                        | The client secret for your app                                                                                                                                                                                                            |
| oauth_scope                                | Enter one or more scope values indicating which parts of the user’s account you wish to access.                                                                                                                                           |
| oauth_grant_type                           | Tines supports `client_credentials` and `authorization_code` grants.                                                                                                                                                                      |
| team_id                                    | ID of Tines Team where the credential will be located.                                                                                                                                                                                    |
| folder_id                                  | **Optional** ID of folder to which the credential will be located                                                                                                                                                                         |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`). default: `TEAM`                                                                                                                                                |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                      |
| description                                | **Optional** Description of the credential. default: `""` (empty string).                                                                                                                                                                 |
| oauthPkceCodeChallengeMethod               | **Optional** PKCE challenge method (`S256`, `plain`). default: `NULL` (None).                                                                                                                                                             |
| metadata                                   | **Optional** Key/value metadata relevant to the credential that can be referenced via the INFO path.                                                                                                                                      |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                |
| live_credential_id                         | **Optional** ID of the live credential                                                                                                                                                                                                    |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "oauth credential",
        "mode": "OAUTH",
        "team_id": 2,
        "oauth_url": "https://example.com/auth",
        "oauth_token_url": "https://example.com/token",
        "oauth_client_id": "foo",
        "oauth_client_secret": "bar",
        "oauth_scope": "sync",
        "oauth_grant_type": "authorization_code"
    }'
```

## Response

A successful request will return a JSON object containing a `redirect_url` to complete the registration process and a description of the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                   |
| shared_team_slugs                          | List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                      |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests                                                                                                                                                     |
| metadata                                   | Key/value metadata relevant to the credential                                                                                                                                                                                |
| redirect_url                               | Redirect URL of the created credential.                                                                                                                                                                                      |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "redirect_url": "",
  "id": 1,
  "name": "tines_api_credential",
  "mode": "OAUTH",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "allowed_hosts": [],
  "metadata": {},
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### Create: Text type

## Description

Use a HTTP POST request to create a [Text](/docs/credentials/text) credential. Please note, for test credentials that will be used in TEST mode of change control, the optional value 'live_credential_id' must be provided. In this scenario, the other optional values (i.e. credential description, read access, team ID, folder ID) will default to the specified live credential ID.

## Request

HTTP Method: **POST**

| Parameter                                  | Description                                                                                                                                                                                                                               |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                       | Name of the credential.                                                                                                                                                                                                                   |
| mode                                       | Describes the type of credential (`TEXT`)                                                                                                                                                                                                 |
| value                                      | Value of the credential.                                                                                                                                                                                                                  |
| team_id                                    | ID of Tines Team where the credential will be located.                                                                                                                                                                                    |
| folder_id                                  | **Optional** ID of folder to which the credential will be located                                                                                                                                                                         |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                   |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                      |
| description                                | **Optional** Description of the credential. default: `""` (empty string).                                                                                                                                                                 |
| metadata                                   | **Optional** Key/value metadata relevant to the credential that can be referenced via the INFO path.                                                                                                                                      |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                |
| live_credential_id                         | **Optional** ID of the live credential                                                                                                                                                                                                    |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "text credential",
        "value": "bar",
        "mode": "TEXT",
        "team_id": 2
    }'
```

## Response

A successful request will return a JSON object describing the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata relevant to the credential                                                                                                                                                                                |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "tines_api_credential",
  "mode": "TEXT",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "allowed_hosts": [],
  "metadata": {},
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### Get

## Description

Retrieve a credential.

## Request

HTTP Method: **GET**

| Parameter     | Description                           |
| ------------- | ------------------------------------- |
| credential_id | The ID of the credential to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/user_credentials/<<credential_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the selected credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name.                                                                                                                                                                        |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| aws_assumed_role_external_id               | External ID for AWS assumed role, if exists.                                                                                                                                                                                 |
| use_static_external_id                     | A boolean value stating if the credential uses a team-scoped static external ID, applicable to AWS credentials                                                                                                               |
| aws_authentication_type                    | The authentication method with AWS, key-based-access or role-based-access(`KEY`, `ROLE`, `INSTANCE_PROFILE`), if exists.                                                                                                     |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata for use in HTTP requests alongside the credential value.                                                                                                                                                  |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 1,
  "name": "tines_github_credential",
  "mode": "TEXT",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_github_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "aws_assumed_role_external_id": null,
  "aws_authentication_type": null,
  "use_static_external_id": false,
  "allowed_hosts": [],
  "metadata": { "account_domain": "https://example.com" },
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

<!-- cspell:enable -->

### Update

## Description

Use a HTTP PUT request to update a [credential](/docs/credentials).

## Request

HTTP Method: **PUT**

| Parameter                                  | Description                                                                                                                                                                                                                                          |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                                                 |
| name                                       | **Optional** Name of the credential.                                                                                                                                                                                                                 |
| folder_id                                  | **Optional** The ID of the folder to move the credential to, or an empty value to indicate the root folder. Note that this folder must exist within the team identified by `team_id`, or the credential's current team if `team_id` isn't specified. |
| read_access                                | **Optional** Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                              |
| shared_team_slugs                          | **Optional** List of teams' slugs where this credential can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                                 |
| description                                | **Optional** Description of the credential. default: `""` (empty string).                                                                                                                                                                            |
| metadata                                   | **Optional** Key/value metadata for use in HTTP requests alongside the credential value                                                                                                                                                              |
| allowed_hosts                              | **Optional** Array of domains where this credential can only be used in HTTP requests. Domain matching supports wildcards.                                                                                                                           |
| test_credential_enabled                    | **Optional** A boolean value stating if the credential is enabled for using a test credential                                                                                                                                                        |
| is_test                                    | **Optional** Boolean value stating if the test credential should be updated. `test_credential_enabled` must be set to `TRUE` and a test credential must exist for the update to succeed.                                                             |
| expires_at                                 | **Optional** ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                             |
| expiry_notifications_enabled               | **Optional** A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders)            |
| credential_notification_recipient_user_ids | **Optional** List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                                 |

#### TEXT Options

| Parameter | Description              |
| --------- | ------------------------ |
| value     | Value of the credential. |

#### OAUTH Options

| Parameter                    | Description                                                                                    |
| ---------------------------- | ---------------------------------------------------------------------------------------------- |
| oauth_url                    | Your app oauth url                                                                             |
| oauth_token_url              | Your app oauth token url                                                                       |
| oauth_client_id              | The client ID for your app                                                                     |
| oauth_client_secret          | The client secret for your app                                                                 |
| oauth_scope                  | Enter one or more scope values indicating which parts of the user’s account you wish to access |
| oauth_grant_type             | Tines supports `client_credentials` and `authorization_code` grants.                           |
| oauthPkceCodeChallengeMethod | PKCE challenge method (`S256`, `plain`). default: `NULL` (None).                               |

#### MTLS Options

| Parameter               | Description                                                                             |
| ----------------------- | --------------------------------------------------------------------------------------- |
| mtls_client_certificate | The certificate file issued by the CA for this client                                   |
| mtls_client_private_key | The private key file for the client certificate                                         |
| mtls_root_certificate   | The root certificate file for the certificate authority (CA) responsible for signatures |

#### JWT Options

| Parameter                     | Description                                     |
| ----------------------------- | ----------------------------------------------- |
| jwt_algorithm                 | The algorithm to be used when computing the JWT |
| jwt_payload                   | The payload to be included in the JWT           |
| jwt_auto_generate_time_claims | Auto generate ‘iat’ & ‘exp’ claims              |
| jwt_private_key               | The private key to be used to sign the JWT      |

#### HTTP_REQUEST_AGENT Options

| Parameter                      | Description                                                                                                      |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| http_request_options           | JSON string representing the Agents::HTTPRequestAgent Options (required)                                         |
| http_request_location_of_token | Location of token from response (required)                                                                       |
| http_request_secret            | Secret value to save in an http request. This can be referenced as `secret` in the HTTP request options builder. |
| http_request_ttl               | Time to live (TTL) in seconds. Not sending this value during an update/PUT operation will update it to null.     |

#### MULTI_REQUEST Options

| Parameter                      | Description                                                                                                  |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| http_request_location_of_token | Location of token from response (required)                                                                   |
| credential_requests            | List of requests to run to fetch the credential values (required).                                           |
| http_request_ttl               | Time to live (TTL) in seconds. Not sending this value during an update/PUT operation will update it to null. |

#### AWS Options

| Parameter               | Description                                                                                                                                |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| aws_authentication_type | The authentication method with AWS, key-based-access or role-based-access(`KEY`, `ROLE`, `INSTANCE_PROFILE`)                               |
| aws_access_key          | The `access key` from your [AWS Security Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)    |
| aws_secret_key          | The `access secret` from your [AWS Security Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) |
| aws_assumed_role_arn    | **Required for role-based-access** The ARN of the role you wish to assume, e.g.: `arn:aws:iam::123456789012:role/write-access-role`        |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/user_credentials/<<credential_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Updated credential name",
        "value": "Updated secret value",
        "mode": "TEXT",
        "metadata": { "account_domain": "https://updatedmetadataexample.com" }
    }'
```

## Response

A successful request will return a JSON object describing the created credential.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name                                                                                                                                                                         |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata for use in HTTP requests alongside the credential value.                                                                                                                                                  |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "tines_api_credential",
  "mode": "TEXT",
  "team_id": 2,
  "folder_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_api_credential",
  "created_at": "2021-03-26T12:34:16.540Z",
  "updated_at": "2021-03-26T12:34:16.540Z",
  "description": "",
  "allowed_hosts": [],
  "metadata": { "account_domain": "https://example.com" },
  "restriction_type": "UNRESTRICTED",
  "test_credential_enabled": false,
  "owner": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io"
  }
}
```

### List

## Description

Retrieve a list of credentials.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| team_id   | **Optional** List actions for the given team                                                        |
| folder_id | **Optional** List actions for the given folder                                                      |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |
| filter    | **Optional** Filter by one of: `UNUSED_IN_ACTIONS`, `UNRESTRICTED`, `RESTRICTED`                    |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing credentials in the Tines tenant.

### Field description

| Parameter                                  | Description                                                                                                                                                                                                                  |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                         | credential ID.                                                                                                                                                                                                               |
| name                                       | Name of the credential.                                                                                                                                                                                                      |
| mode                                       | Describes the type of credential (`TEXT, JWT, OAUTH, AWS, MTLS, HTTP_REQUEST_AGENT, MULTI_REQUEST`).                                                                                                                         |
| team_id                                    | ID of team to which the credential belongs.                                                                                                                                                                                  |
| folder_id                                  | ID of folder to which the credential belongs.                                                                                                                                                                                |
| read_access                                | Control where this credential can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                                              |
| shared_team_slugs                          | List of teams' slugs where this credential can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                                              |
| description                                | Description of the credential.                                                                                                                                                                                               |
| slug                                       | An underscored representation of the credential name.                                                                                                                                                                        |
| created_at                                 | ISO 8601 Timestamp representing date and time the credential was created.                                                                                                                                                    |
| updated_at                                 | ISO 8601 Timestamp representing date and time the credential was last updated.                                                                                                                                               |
| aws_assumed_role_external_id               | External ID for AWS assumed role, if exists.                                                                                                                                                                                 |
| aws_authentication_type                    | The authentication method with AWS, key-based-access or role-based-access(`KEY`, `ROLE`, `INSTANCE_PROFILE`), if exists.                                                                                                     |
| allowed_hosts                              | Array of domains where this credential can only be used in HTTP requests.                                                                                                                                                    |
| metadata                                   | Key/value metadata for use in HTTP requests alongside the credential value.                                                                                                                                                  |
| restriction_type                           | The type of restriction applied to the use of the credential (`RESTRICTED`,`RESTRICTED_TO_CREDENTIALS` ,`UNRESTRICTED` )                                                                                                     |
| test_credential_enabled                    | A boolean value stating if the credential is enabled for using a test credential                                                                                                                                             |
| test_credential                            | Data specific to the test credential (`created_at` and `updated_at`)                                                                                                                                                         |
| owner                                      | An object representing the user who owns this credential. By default, the owner is the user who created the credential.                                                                                                      |
| expires_at                                 | ISO 8601 Timestamp representing date and time the credential will expire. Default: `null`. See: [Credential expiry](https://www.tines.com/docs/credentials/credential-configuration/expiry)                                  |
| expiry_notifications_enabled               | A boolean value stating whether or not expiry notifications are enabled. Default: `false`. See: [Credential expiry email reminders](https://www.tines.com/docs/credentials/credential-configuration/expiry/#email-reminders) |
| credential_notification_recipient_user_ids | List of user IDs that will be sent credential notifications (e.g. expiry notifications)                                                                                                                                      |

### Sample response

<!-- cspell:disable -->

```json
{
  "user_credentials": [
    {
      "id": 1,
      "name": "tines_github_credential",
      "mode": "TEXT",
      "team_id": 2,
      "folder_id": 1,
      "read_access": "TEAM",
      "shared_team_slugs": [],
      "slug": "tines_github_credential",
      "created_at": "2021-03-26T12:34:16.540Z",
      "updated_at": "2021-03-26T12:34:16.540Z",
      "description": "",
      "aws_assumed_role_external_id": null,
      "aws_authentication_type": null,
      "allowed_hosts": [],
      "metadata": {},
      "restriction_type": "UNRESTRICTED",
      "test_credential_enabled": false,
      "owner": {
        "user_id": 1,
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@tines.io"
      }
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/user_credentials?per_page=1&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 1,
    "pages": 1,
    "count": 1
  }
}
```

<!-- cspell:enable -->

### Delete

## Description

Delete a credential.

## Request

HTTP Method: **DELETE**

| Parameter     | Description                         |
| ------------- | ----------------------------------- |
| credential_id | The ID of the credential to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/user_credentials/<<credential_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON object with a `null` body and a `204` status code.

## Folders

### Create a Folder

## Description

Create a folder in Tines.

## Request

HTTP Method: **POST**

| Query Parameter  | Description                                                                  |
| ---------------- | ---------------------------------------------------------------------------- |
| name             | The folder name.                                                             |
| content_type     | The type of content stored in this folder `CREDENTIAL` `RESOURCE` or `STORY` |
| team_id          | ID of team to which the folder should be created.                            |
| parent_folder_id | **Optional** ID of the parent folder. Default: `null` (root level).          |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/folders/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "name": "Folder name",
      "content_type": "CREDENTIAL",
      "team_id": 1,
      "parent_folder_id": 5
    }'
```

## Response

A successful request will return a JSON object describing the created folder

### Field description

| Parameter        | Description                                              |
| ---------------- | -------------------------------------------------------- |
| id               | The folder ID.                                           |
| name             | The folder name.                                         |
| team_id          | ID of team to which the folder belongs.                  |
| content_type     | The type of content stored in this folder                |
| size             | The number of items in this folder                       |
| parent_folder_id | ID of the parent folder, or `null` if at the root level. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 1,
  "name": "Folder name",
  "content_type": "CREDENTIAL",
  "team_id": 1,
  "size": 0,
  "parent_folder_id": 5
}
```

<!-- cspell:enable -->

### Get a Folder

## Description

Retrieve a single folder.

## Request

HTTP Method: **GET**

| Parameter | Description                       |
| --------- | --------------------------------- |
| folder_id | The ID of the folder to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/folders/<<folder_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified folder.

### Field description

| Parameter        | Description                                              |
| ---------------- | -------------------------------------------------------- |
| id               | The folder ID.                                           |
| name             | The folder name.                                         |
| team_id          | ID of team to which the folder belongs.                  |
| content_type     | The type of content stored in this folder                |
| size             | The number of items in this folder                       |
| parent_folder_id | ID of the parent folder, or `null` if at the root level. |

### Sample response

```json
{
  "id": 1,
  "name": "Folder name",
  "content_type": "CREDENTIAL",
  "team_id": 1,
  "size": 0,
  "parent_folder_id": null
}
```

### Update a Folder

## Description

Update a folder.

## Request

HTTP Method: **PUT**

| Query Parameter  | Description                                                                |
| ---------------- | -------------------------------------------------------------------------- |
| name             | The folder name.                                                           |
| parent_folder_id | **Optional** ID of the parent folder. Set to `null` to move to root level. |

| Path Parameter | Description      |
| -------------- | ---------------- |
| folder_id      | ID of the folder |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/folders/<<folder_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "name": "New folder name",
        "parent_folder_id": 5
      }'
```

## Response

A successful request will return a JSON object describing the updated folder

### Field description

| Parameter        | Description                                              |
| ---------------- | -------------------------------------------------------- |
| id               | The folder ID.                                           |
| name             | The folder name.                                         |
| team_id          | ID of team to which the folder belongs.                  |
| content_type     | The type of content stored in this folder                |
| size             | The number of items in this folder                       |
| parent_folder_id | ID of the parent folder, or `null` if at the root level. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 1,
  "name": "New folder name",
  "content_type": "CREDENTIAL",
  "team_id": 1,
  "size": 0,
  "parent_folder_id": 5
}
```

<!-- cspell:enable -->

### List Folders

## Description

Retrieve a list of folders.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** ID of team to which the folders belong.                                                |
| content_type    | **Optional** The type of content stored in the folders.                                             |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/folders \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing folders in the Tines tenant.

### Field description

| Parameter        | Description                                              |
| ---------------- | -------------------------------------------------------- |
| id               | The folder ID.                                           |
| name             | The folder name.                                         |
| team_id          | ID of team to which the folder belongs.                  |
| content_type     | The type of content stored in the folder                 |
| size             | The number of items in this folder                       |
| parent_folder_id | ID of the parent folder, or `null` if at the root level. |

### Sample response

<!-- cspell:disable -->

```json
{
  "folders": [
    {
      "id": 1,
      "name": "Credential folder",
      "content_type": "CREDENTIAL",
      "team_id": 1,
      "size": 1,
      "parent_folder_id": null
    },
    {
      "id": 2,
      "name": "Resource folder",
      "content_type": "RESOURCE",
      "team_id": 1,
      "size": 1,
      "parent_folder_id": null
    },
    {
      "id": 3,
      "name": "Story folder",
      "content_type": "STORY",
      "team_id": 1,
      "size": 5,
      "parent_folder_id": 1
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/folders?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  }
}
```

<!-- cspell:enable -->

### Delete Folder

## Description

Delete a folder.

## Request

HTTP Method: **DELETE**

| Parameter | Description                     |
| --------- | ------------------------------- |
| folder_id | The ID of the folder to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/folders/<<folder_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

## Resources

### Create: file type

## Description

Create a file resource with Base64-encoded content.

## File value object

The `value` parameter must be a JSON object with:

| Field    | Required | Description                         |
| -------- | -------- | ----------------------------------- |
| name     | Yes      | File name (e.g., "document.pdf")    |
| contents | Yes      | Base64-encoded file contents        |
| type     | No       | MIME type (e.g., "application/pdf") |

## Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "my_file_resource",
        "team_id": 2,
        "value": {
          "name": "example.txt",
          "contents": "SGVsbG8gV29ybGQh",
          "type": "text/plain"
        }
    }'
```

For all parameters and response fields, see [Create: text type](/api/resources/create-text).

### Create: JSON type

## Description

Create a JSON resource (object or array).

## Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "a json resource",
        "value": {"fizz":"buzz"},
        "team_id": 2,
        "folder_id": 1
    }'
```

For all parameters and response fields, see [Create: text type](/api/resources/create-text).

### Create: text type

## Description

Create a text resource in Tines. Please note, for test resources that will be used in TEST mode of change control, the optional value 'Live Resource ID' must be provided. In this scenario, the other optional values (i.e. resource description, read access, team ID, folder ID) will default to the specified live resource ID.

## Request

HTTP Method: **POST**

| Parameter                      | Description                                                                                                                                                                                                           |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                           | Name of the resource.                                                                                                                                                                                                 |
| value                          | Contents of the resource. When a non-JSON string is passed in, it will be parsed as regular text.                                                                                                                     |
| team_id                        | ID of Tines Team where the resource will be located.                                                                                                                                                                  |
| folder_id                      | **Optional** ID of folder to which the resource will be located                                                                                                                                                       |
| read_access                    | **Optional** Control where this resource can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).) |
| shared_team_slugs              | **Optional** List of teams' slugs where this resource can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                    |
| description                    | **Optional** Description of the resource. default: `""` (empty string)                                                                                                                                                |
| live_resource_id               | **Optional** The id of live resource that corresponds to the new test value                                                                                                                                           |
| include_referencing_action_ids | **Optional** Defaults to `true`, when set to `false` we will exclude `referencing_action_ids` from the response                                                                                                       |
| typed_value                    | **Optional** Defaults to `false`. When set to `true`, returns properly typed values (boolean, number, array, object) instead of strings                                                                               |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "foo",
        "value": "bar"
    }'
```

## Response

A successful request will return a JSON object describing the created resource. Test resource data is contained within the live resource.

### Field description

| Parameter              | Description                                                                                                                                               |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                     | Resource ID.                                                                                                                                              |
| name                   | Name of the resource.                                                                                                                                     |
| value                  | Value of the resource. By default, returns a string representation. Use `typed_value=true` to get properly typed values (boolean, number, array, object). |
| team_id                | ID of team to which the resource belongs.                                                                                                                 |
| folder_id              | ID of folder to which the resource belongs.                                                                                                               |
| user_id                | ID of user associated with the resource.                                                                                                                  |
| read_access            | Control where this resource can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                             |
| shared_team_slugs      | List of teams' slugs where this resource can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| slug                   | An underscored representation of the resource name                                                                                                        |
| created_at             | ISO 8601 Timestamp representing date and time the resource was created.                                                                                   |
| updated_at             | ISO 8601 Timestamp representing date and time the resource was last updated.                                                                              |
| description            | Description of the resource.                                                                                                                              |
| test_resource_enabled  | A boolean value stating if the resource is enabled for using a test resource.                                                                             |
| test_resource          | JSON block of the test resource.                                                                                                                          |
| referencing_action_ids | IDs of action that are referencing the Resource.                                                                                                          |

### Sample response

```json
{
  "id": 9,
  "name": "foo",
  "value": "bar",
  "team_id": 2,
  "folder_id": 1,
  "user_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "foo",
  "created_at": "2020-02-05T20:40:32.313Z",
  "updated_at": "2020-02-05T20:55:18.239Z",
  "description": "",
  "test_resource_enabled": false
}
```

### Get

## Description

Retrieve a resource.

## Request

HTTP Method: **GET**

| Parameter                      | Description                                                                                                                             |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| resource_id                    | The ID of the live resource to retrieve.                                                                                                |
| include_referencing_action_ids | **Optional** Defaults to `true`, when set to `false` we will exclude `referencing_action_ids` from the response                         |
| typed_value                    | **Optional** Defaults to `false`. When set to `true`, returns properly typed values (boolean, number, array, object) instead of strings |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

### Example with typed_value

To retrieve a resource with properly typed values (e.g., `true` as a boolean instead of `"true"` as a string):

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>?typed_value=true \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the selected resource.

### Field description

| Parameter              | Description                                                                                                                                                                                  |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                     | Resource ID.                                                                                                                                                                                 |
| name                   | Name of the resource.                                                                                                                                                                        |
| value                  | Value of the resource. By default, returns a string representation. Use `typed_value=true` to get properly typed values. For file resources, contains `name`, `contents`, and `type` fields. |
| team_id                | ID of team to which the resource belongs.                                                                                                                                                    |
| folder_id              | ID of folder to which the resource belongs.                                                                                                                                                  |
| user_id                | ID of user associated with the resource.                                                                                                                                                     |
| read_access            | Control where this resource can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                                                                |
| shared_team_slugs      | List of teams' slugs where this resource can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                                |
| slug                   | An underscored representation of the resource name                                                                                                                                           |
| created_at             | ISO 8601 Timestamp representing date and time the resource was created.                                                                                                                      |
| updated_at             | ISO 8601 Timestamp representing date and time the resource was last updated.                                                                                                                 |
| description            | Description of the resource.                                                                                                                                                                 |
| test_resource_enabled  | A boolean value stating if the resource is enabled for using a test resource.                                                                                                                |
| test_resource          | JSON block of the test resource.                                                                                                                                                             |
| referencing_action_ids | IDs of action that are referencing the Resource.                                                                                                                                             |

### Sample response

<!-- cspell:disable -->

**Default response (typed_value=false or omitted):**

```json
{
  "id": 1,
  "name": "tines_jira_url",
  "value": "tinesio.atlassian.net",
  "team_id": 2,
  "folder_id": 1,
  "user_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "tines_jira_url",
  "created_at": "2019-12-12T21:34:16.540Z",
  "updated_at": "2019-12-12T21:34:16.540Z",
  "description": "",
  "test_resource_enabled": false,
  "referencing_action_ids": [431]
}
```

**Response with typed_value=true:**

When `typed_value=true` is specified, values are returned with their proper types:

```json
{
  "id": 2,
  "name": "enable_feature",
  "value": true,
  "team_id": 2,
  "folder_id": 1,
  "user_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "enable_feature",
  "created_at": "2019-12-12T21:34:16.540Z",
  "updated_at": "2019-12-12T21:34:16.540Z",
  "description": "",
  "test_resource_enabled": false,
  "referencing_action_ids": [432]
}
```

Note: In the example above, `value` is a boolean `true` rather than the string `"true"`. This applies to all value types:

- Booleans: `true` / `false` (not `"true"` / `"false"`)
- Numbers: `42` (not `"42"`)
- Arrays: `["a", "b"]` (not `"[\"a\",\"b\"]"`)
- Objects: `{"key": "value"}` (not `"{\"key\":\"value\"}"`)
- Strings: remain as strings

<!-- cspell:enable -->

### Update

## Description

Use a HTTP PUT request to update a resource. If the resource has a test value this can be modified by using the is_test parameter.

## Request

HTTP Method: **PUT**

| Parameter                      | Description                                                                                                                                                                                                                                      |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| resource_id                    | The ID of the live resource.                                                                                                                                                                                                                     |
| value                          | Contents of the resource. For file resources, pass a JSON object with file data (see below).                                                                                                                                                     |
| name                           | **Optional** Name of the resource.                                                                                                                                                                                                               |
| folder_id                      | **Optional** The ID of the folder to move the resource to, or an empty value to indicate the root folder. Note that this folder must exist within the team identified by `team_id`, or the resource's current team if `team_id` isn't specified. |
| read_access                    | **Optional** Control where this resource can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default: `TEAM`. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                            |
| shared_team_slugs              | **Optional** List of teams' slugs where this resource can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. default: `[]` (empty array).                                                                                               |
| description                    | **Optional** Description of the resource. default: `""` (empty string).                                                                                                                                                                          |
| test_resource_enabled          | **Optional** Boolean value stating if the resource is enabled for using a test. resource                                                                                                                                                         |
| is_test                        | **Optional** Boolean value stating if the test resource should be updated. `test_resource_enabled` must be set to `TRUE` and a test resource must exist for the update to succeed.                                                               |
| include_referencing_action_ids | **Optional** Defaults to `true`, when set to `false` we will exclude `referencing_action_ids` from the response                                                                                                                                  |
| typed_value                    | **Optional** Defaults to `false`. When set to `true`, returns properly typed values (boolean, number, array, object) instead of strings                                                                                                          |

### File value object

| Field    | Required | Description                         |
| -------- | -------- | ----------------------------------- |
| name     | Yes      | File name (e.g., "document.pdf")    |
| contents | Yes      | Base64-encoded file contents        |
| type     | No       | MIME type (e.g., "application/pdf") |

### Sample request (text resource)

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value":"updated resource value"
    }'
```

### Sample request (file resource)

<!-- cspell:disable -->

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": {
          "name": "updated_document.pdf",
          "contents": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PAovQ3...",
          "type": "application/pdf"
        }
    }'
```

<!-- cspell:enable -->

## Response

A successful request will return a JSON object describing the updated resource. Test resource data is contained within the live resource.

### Field description

| Parameter              | Description                                                                                                                                               |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                     | Resource ID.                                                                                                                                              |
| name                   | Name of the resource.                                                                                                                                     |
| value                  | Value of the resource. By default, returns a string representation. Use `typed_value=true` to get properly typed values (boolean, number, array, object). |
| team_id                | ID of team to which the resource belongs.                                                                                                                 |
| folder_id              | ID of folder to which the resource belongs.                                                                                                               |
| user_id                | ID of user associated with the resource.                                                                                                                  |
| read_access            | Control where this resource can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                             |
| shared_team_slugs      | List of teams' slugs where this resource can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| slug                   | An underscored representation of the resource name                                                                                                        |
| created_at             | ISO 8601 Timestamp representing date and time the resource was created.                                                                                   |
| updated_at             | ISO 8601 Timestamp representing date and time the resource was last updated.                                                                              |
| description            | Description of the resource.                                                                                                                              |
| test_resource_enabled  | A boolean value stating if the resource is enabled for using a test resource.                                                                             |
| test_resource          | JSON block of the test resource.                                                                                                                          |
| referencing_action_ids | IDs of action that are referencing the Resource.                                                                                                          |

### Sample response

```json
{
  "id": 9,
  "name": "an array resource",
  "value": "updated resource value",
  "team_id": 1,
  "folder_id": 1,
  "user_id": 1,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "slug": "an_array_resource",
  "created_at": "2020-02-05T20:40:32.313Z",
  "updated_at": "2020-02-05T20:55:18.239Z",
  "description": "",
  "test_resource_enabled": false
}
```

### List

## Description

Retrieve a list of resources.

## Request

HTTP Method: **GET**

| Query Parameter                | Description                                                                                                                             |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| team_id                        | **Optional** Return resources belonging to this team.                                                                                   |
| folder_id                      | **Optional** Return resources in this folder.                                                                                           |
| per_page                       | **Optional** Set the number of results returned per page.                                                                               |
| page                           | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                     |
| filter                         | **Optional** Filter: `UNUSED_IN_ACTIONS`                                                                                                |
| include_referencing_action_ids | **Optional** Defaults to `true`, when set to `false` we will exclude `referencing_action_ids` from the response                         |
| typed_value                    | **Optional** Defaults to `false`. When set to `true`, returns properly typed values (boolean, number, array, object) instead of strings |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/global_resources \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing resources in the Tines tenant.\
Results are paginated by default to 20 results returned per page.

### Field description

| Parameter              | Description                                                                                                                                               |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                     | Resource ID.                                                                                                                                              |
| name                   | Name of the resource.                                                                                                                                     |
| value                  | Value of the resource. By default, returns a string representation. Use `typed_value=true` to get properly typed values (boolean, number, array, object). |
| team_id                | ID of team to which the resource belongs.                                                                                                                 |
| folder_id              | ID of folder to which the resource belongs.                                                                                                               |
| user_id                | ID of user associated with the resource.                                                                                                                  |
| read_access            | Control where this resource can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                                                             |
| shared_team_slugs      | List of teams' slugs where this resource can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| slug                   | An underscored representation of the resource name                                                                                                        |
| created_at             | ISO 8601 Timestamp representing date and time the resource was created.                                                                                   |
| updated_at             | ISO 8601 Timestamp representing date and time the resource was last updated.                                                                              |
| description            | Description of the resource.                                                                                                                              |
| test_resource_enabled  | A boolean value stating if the resource is enabled for using a test resource.                                                                             |
| test_resource          | JSON block of the test resource.                                                                                                                          |
| referencing_action_ids | IDs of action that are referencing the Resource.                                                                                                          |

### Sample response

<!-- cspell:disable -->

```json
{
  "global_resources": [
    {
      "id": 1,
      "name": "tines_jira_url",
      "value": "tinesio.atlassian.net",
      "team_id": 2,
      "folder_id": 1,
      "user_id": 1,
      "read_access": "TEAM",
      "shared_team_slugs": [],
      "slug": "tines_jira_url",
      "created_at": "2019-12-12T21:34:16.540Z",
      "updated_at": "2019-12-12T21:34:16.540Z",
      "description": "",
      "test_resource_enabled": true,
      "test_resource": {
        "value": "test resource",
        "created_at": "2019-12-12T22:34:16.540Z",
        "updated_at": "2019-12-12T22:34:16.540Z"
      },
      "referencing_action_ids": [431]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/global_resources?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

<!-- cspell:enable -->

### Remove element

## Description

Remove a top level element from an array or key from an object in a resource. The request returns the removed item. If the resource has a test value this can be modified by using the is_test parameter.

## Request

HTTP Method: **POST**

| Parameter   | Description                                                                                                                                                                                                                                                                                                                                                                                                               |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| resource_id | The ID of the live resource.                                                                                                                                                                                                                                                                                                                                                                                              |
| key         | The object key to remove if removing from an object.                                                                                                                                                                                                                                                                                                                                                                      |
| index       | The index of the element to remove if removing from an array (with indexes starting at 0). If omitted the last element of the array will be removed                                                                                                                                                                                                                                                                       |
| if_value    | **Optional** When provided, the operation only proceeds if the current value at the specified key (for objects) or index (for arrays) matches this value. If the value does not match, a 422 error is returned with the current value included in the response. Supports any JSON value (string, number, boolean, object, array). A `null` value is treated the same as omitting the parameter (unconditional operation). |
| is_test     | **Optional** Boolean value stating if the test resource should be used                                                                                                                                                                                                                                                                                                                                                    |

### Sample request

Sample request for an array element

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/remove \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "index":"0"
    }'
```

Sample request for an object key

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/remove \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "key":"foo"
    }'
```

Sample request for conditional removal (only removes if current value matches)

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/remove \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "key": "locked_by",
        "if_value": "run-123"
    }'
```

## Response

A successful request will return the removed item from the resource in string format.

### Sample response

```json
"foo bar"
```

### Delete

## Description

Delete a resource.

## Request

HTTP Method: **DELETE**

| Parameter   | Description                            |
| ----------- | -------------------------------------- |
| resource_id | The ID of the live resource to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Append element

## Description

Append a string or an array to a resource. If the resource has a test value this can be modified by using the is_test parameter.

## Request

HTTP Method: **POST**

| Parameter   | Description                                                            |
| ----------- | ---------------------------------------------------------------------- |
| resource_id | The ID of the live resource.                                           |
| value       | The string or an array to append to the resource.                      |
| is_test     | **Optional** Boolean value stating if the test resource should be used |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/append \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value":"[1,2,3,4,5]"
    }'
```

## Response

A successful request will return the updated resource value

### Sample response

```json
[1, 2, 3, 4, 5]
```

### Replace element

## Description

Replace a top level element from an array or key from an object in a resource. The request returns the updated resource. If the resource has a test value this can be modified by using the is_test parameter.

## Request

HTTP Method: **POST**

| Parameter   | Description                                                                                                                                                                                                                                                                                                                                                                                                               |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| resource_id | The ID of the live resource.                                                                                                                                                                                                                                                                                                                                                                                              |
| key         | The object key to replace if replacing from an object.                                                                                                                                                                                                                                                                                                                                                                    |
| index       | The index of the element to replace if replacing from an array (with indexes starting at 0).                                                                                                                                                                                                                                                                                                                              |
| value       | The new value to replace the existing element with                                                                                                                                                                                                                                                                                                                                                                        |
| if_value    | **Optional** When provided, the operation only proceeds if the current value at the specified key (for objects) or index (for arrays) matches this value. If the value does not match, a 422 error is returned with the current value included in the response. Supports any JSON value (string, number, boolean, object, array). A `null` value is treated the same as omitting the parameter (unconditional operation). |
| is_test     | **Optional** Boolean value stating if the test resource should be used                                                                                                                                                                                                                                                                                                                                                    |

### Sample request

Sample request for an array element

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/replace \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "index": "0",
        "value": "new-value"
    }'
```

Sample request for an object key

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/replace \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "key": "foo",
        "value": "new-value"
    }'
```

Sample request for conditional replacement (compare-and-swap)

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/replace \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "key": "count",
        "value": 6,
        "if_value": 5
    }'
```

## Examples

### Replacing an element in an array

Given a resource with value:

```json
["alice", "bob", "charlie"]
```

Sending `{ "index": "1", "value": "eve" }` updates the resource to:

```json
["alice", "eve", "charlie"]
```

### Replacing a key in an object

Given a resource with value:

```json
{ "city": "Cork", "country": "Ireland" }
```

Sending `{ "key": "city", "value": "Galway" }` updates the resource to:

```json
{ "city": "Galway", "country": "Ireland" }
```

### Conditional replacement (compare-and-swap)

Given a resource with value:

```json
{ "count": 5 }
```

Sending `{ "key": "count", "value": 6, "if_value": 5 }` updates the resource to:

```json
{ "count": 6 }
```

If `count` were any other value, the request would return a 422 error with the current value and the resource would remain unchanged.

## Response

A successful request will return the updated resource value.

### Sample response

```json
{ "city": "Galway", "country": "Ireland" }
```

### Lock/unlock

## Description

Lock or unlock a resource. Locking a resource prevents its value from being modified. Requires `RESOURCE_MANAGE` permission.

Unlocking via the API is **permanent** — the resource remains unlocked until explicitly locked again. This differs from the Tines UI, which offers a temporary unlock that automatically re-locks the resource after a single edit.

Locking or unlocking a resource also applies to its test resource. A test resource cannot be locked or unlocked independently from its live resource.

## Request

HTTP Method: **PUT**

| Parameter | Description                                             |
| --------- | ------------------------------------------------------- |
| id        | The ID of the resource.                                 |
| locked    | Boolean. `true` to lock the resource, `false` to unlock |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/global_resources/<<resource_id>>/locked \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"locked": true}'
```

## Response

A successful request will return a JSON object with the resource's `id` and `locked` status.

### Sample response

```json
{
  "id": 9,
  "locked": true
}
```

## Stories

### Create

## Description

Create a story in Tines.

## Request

HTTP Method: **POST**

| Query Parameter | Description                                                                                                                                                                                                                                                                                                            |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id         | ID of team to which the story should be added.                                                                                                                                                                                                                                                                         |
| name            | **Optional** The story name.                                                                                                                                                                                                                                                                                           |
| description     | **Optional** A user-defined description of the story.                                                                                                                                                                                                                                                                  |
| keep_events_for | **Optional** Event retention period in seconds: <br /> 1 hour: `3600`<br /> 6 hours: `21600`<br /> 1 day: `86400`<br />3 days: `259200`<br />7 days: `604800`<br /> 14 days: `1209600 `<br/> 30 days: `2592000`<br /> 60 days: `5184000`<br /> 90 days: `7776000`<br /> 180 days: `15552000`<br />365 days: `31536000` |
| folder_id       | **Optional** ID of folder to which the story should be added.                                                                                                                                                                                                                                                          |
| tags            | **Optional** An array of Strings used to create tags to classify the story.                                                                                                                                                                                                                                            |
| disabled        | **Optional** Boolean flag indicating whether the story is disabled (default: `false`)                                                                                                                                                                                                                                  |
| priority        | **Optional** Boolean flag indicating if this is a high priority story (default: `false`).                                                                                                                                                                                                                              |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Simple story",
        "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
        "team_id": 1,
        "folder_id": 1
      }'
```

## Response

A successful request will return a JSON object describing the created story

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                    |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name.                                                                                                               |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | The mode of the story. `LIVE` or `TEST`                                                                                                                        |
| id                                            | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on all actions in the story. This will be false when only some actions have monitoring enabled.         |
| actions_with_monitoring                       | List of action IDs with monitoring enabled.                                                                                                                    |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                   |

### Sample response

<!-- cspell:disable -->

```json
{
  "name": "Simple story",
  "user_id": 167,
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "send_to_story_skill_use_requires_confirmation": true,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "slug": "simple_story",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "LIVE",
  "id": 7981,
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1],
  "monitor_failures": false,
  "actions_with_monitoring": [],
  "recipients": [
    {
      "address": "test@example.com"
    }
  ]
}
```

<!-- cspell:enable -->

### Get

## Description

Retrieve a single story.

## Request

HTTP Method: **GET**

| Path Parameter | Description                      |
| -------------- | -------------------------------- |
| story_id       | The ID of the story to retrieve. |

| Query Parameter       | Description                                                                                                                                                                   |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| story_mode            | **Optional** The mode (`TEST` or `LIVE`) of the story to retrieve.                                                                                                            |
| draft_id              | **Optional** The ID of the draft to retrieve. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints).                                               |
| include_live_activity | **Optional** When set to `true`, includes live activity metrics like `pending_action_runs_count`, `concurrent_runs_count`, and `tokens_used_percentage`. Defaults to `false`. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/stories/<<story_id>>?include_live_activity=true \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified story.

### Field description

| Parameter                                     | Description                                                                                                                                                                            |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                                        |
| user_id                                       | ID of story creator.                                                                                                                                                                   |
| description                                   | A user-defined description of the story.                                                                                                                                               |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                                             |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                                                     |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                                          |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                                                   |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                                                       |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                                                      |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                                                     |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                     |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                                             |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                                            |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                                                       |
| team_id                                       | ID of team to which the story belongs.                                                                                                                                                 |
| tags                                          | An array of tags used to classify the story.                                                                                                                                           |
| guid                                          | Unique identifier of the story.                                                                                                                                                        |
| slug                                          | An underscored representation of the story name.                                                                                                                                       |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                                                   |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself.                         |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                                                    |
| mode                                          | The mode of the story. `LIVE` or `TEST`                                                                                                                                                |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on all actions in the story. This will be false when only some actions have monitoring enabled.                                 |
| actions_with_monitoring                       | List of action IDs with monitoring enabled.                                                                                                                                            |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                                           |
| api_enabled                                   | Boolean flag indicating if Webhook API is enabled.                                                                                                                                     |
| api_entry_actions                             | Array of entry action ID for API enabled story.                                                                                                                                        |
| api_exit_actions                              | Array of exit action IDs for API enabled story.                                                                                                                                        |
| api_name                                      | Name of API for API enabled story.                                                                                                                                                     |
| id                                            | The story ID.                                                                                                                                                                          |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                                               |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                                                |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                                                  |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                                        |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                                               |
| draft_id                                      | ID of the selected draft.                                                                                                                                                              |
| draft_name                                    | Name of the selected draft.                                                                                                                                                            |
| pending_action_runs_count                     | Total number of pending action runs across all actions in the story. Only included when `include_live_activity=true` is set.                                                           |
| concurrent_runs_count                         | Number of concurrent story runs currently executing. Only included when `include_live_activity=true` is set.                                                                           |
| tokens_used_percentage                        | Percentage of token limit used by the story. Only included when `include_live_activity=true` is set.                                                                                   |
| not_working_actions_count                     | Number of actions in a "Not working" state in the story, meaning the action has not emitted an event since its last error log. Only included when `include_live_activity=true` is set. |

### Sample response

```json
{
  "name": "Simple story",
  "user_id": 167,
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "send_to_story_skill_use_requires_confirmation": true,
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "slug": "simple_story",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "LIVE",
  "monitor_failures": false,
  "actions_with_monitoring": [],
  "recipients": [
    {
      "address": "test@example.com"
    }
  ],
  "api_enabled": false,
  "api_entry_actions": [],
  "api_exit_actions": [],
  "api_name": null,
  "id": 7981,
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1],
  "draft_id": 1234,
  "draft_name": "Initial Draft",
  "pending_action_runs_count": 150,
  "concurrent_runs_count": 3,
  "tokens_used_percentage": 15.7
}
```

### Update

## Description

Update a story. Defaults to a draft called `test` if change control is enabled on the story and no draft_id is provided.

## Request

HTTP Method: **PUT**

| Query Parameter                               | Description                                                                                                                                                                                                                                                                                                        |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| name                                          | **Optional** The story name.                                                                                                                                                                                                                                                                                       |
| description                                   | **Optional** A user-defined description of the story.                                                                                                                                                                                                                                                              |
| add_tag_names                                 | **Optional** An array of tag names to add to the story.                                                                                                                                                                                                                                                            |
| remove_tag_names                              | **Optional** An array of tag names to remove from the story.                                                                                                                                                                                                                                                       |
| keep_events_for                               | **Optional** Event retention period in seconds: <br /> 1 hour: `3600`<br /> 6 hours: `21600`<br /> 1 day: `86400`<br />3 days: `259200`<br />7 days: `604800`<br /> 14 days: `1209600 `<br/> 30 days:`2592000`<br /> 60 days:`5184000`<br /> 90 days:`7776000`<br /> 180 days:`15552000`<br />365 days: `31536000` |
| disabled                                      | **Optional** Boolean flag indicating whether the story is disabled from running.                                                                                                                                                                                                                                   |
| locked                                        | **Optional** Boolean flag indicating whether the story is locked, preventing edits.                                                                                                                                                                                                                                |
| priority                                      | **Optional** Boolean flag indicating whether story runs with high priority.                                                                                                                                                                                                                                        |
| webhook_api_enabled                           | **Optional** Boolean flag indicating if webhook API is enabled. If enabling, the `api_entry_action_id` and `api_exit_action_ids` parameters must also be specified. The `draft_id` can also be provided if the change is being made to a non-live story.                                                           |
| api_entry_action_id                           | **Optional** The ID of the entry action for this API enabled story (action must be of type webhook). `api_exit_action_ids` must also be provided and `webhook_api_enabled` must be set to true.                                                                                                                    |
| api_exit_action_ids                           | **Optional** An array of IDs describing exit actions for this API enabled story (actions must be message-only mode event transformation). `api_entry_action_id` must also be provided and `webhook_api_enabled` must be set to true.                                                                               |
| send_to_story_access_source                   | **Optional** `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating if send to story is enabled and where the send to story can be used. If enabling send to story, the `entry_action_id` and `exit_action_ids` parameters must also be specified.                                                            |
| entry_action_id                               | **Optional** The ID of the entry action for this send to story enabled story (action must be of type webhook). `exit_action_ids` must also be provided and `send_to_story_access_source` must be set to `STS`, `STS_AND_WORKBENCH` or `WORKBENCH`.                                                                 |
| exit_action_ids                               | **Optional** An array of IDs describing exit actions for this send to story enabled story (actions must be message-only mode event transformation). `entry_action_id` must also be provided and `send_to_story_access_source` must be set to `STS`, `STS_AND_WORKBENCH` or `WORKBENCH`.                            |
| send_to_story_access                          | **Optional** Controls who is allowed to send to this story (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). default to `TEAM`. If using `SPECIFIC_TEAMS` must also specify list of teams' slugs in `shared_team_slugs`.                                                                                                       |
| shared_team_slugs                             | **Optional** List of teams' slugs that can send to this story. Required to set `send_to_story_access` to `SPECIFIC_TEAMS`. Defaults to `[]` (empty array).                                                                                                                                                         |
| send_to_story_skill_use_requires_confirmation | **Optional** Boolean flag indicating whether workbench should ask for confirmation before running this story. Story must be enabled for workbench or send to story and workbench.                                                                                                                                  |
| team_id                                       | **Optional** The ID of the team to move the story to.                                                                                                                                                                                                                                                              |
| folder_id                                     | **Optional** The ID of the folder to move the story to, or an empty value to indicate the root folder. Note that this folder must exist within the team identified by `team_id`, or the story's current team if `team_id` isn't specified.                                                                         |
| change_control_enabled                        | **Optional** Boolean flag indicating if change control is enabled.                                                                                                                                                                                                                                                 |
| draft_id                                      | **Optional** The ID of the draft to update. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints).                                                                                                                                                                                      |
| monitor_failures                              | **Optional** Boolean flag indicating if monitor failures is enabled on the story.                                                                                                                                                                                                                                  |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/stories/<<story_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Updated Simple story",
        "description": "Updates story description"
      }'
```

## Response

A successful request will return a JSON object describing the updated story

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if send to story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An array of objects describing exit actions for this story.                                                                                                    |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name.                                                                                                               |
| created_at                                    | ISO 8601 timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | The mode of the story. `LIVE` or `TEST`                                                                                                                        |
| id                                            | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |
| draft_id                                      | ID of the updated draft.                                                                                                                                       |
| draft_name                                    | Name of the updated draft.                                                                                                                                     |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on all actions in the story. This will be false when only some actions have monitoring enabled.         |
| actions_with_monitoring                       | List of action IDs with monitoring enabled.                                                                                                                    |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                   |

### Sample response

<!-- cspell:disable -->

```json
{
  "name": "Updated Simple story",
  "user_id": 167,
  "description": "Updates story description",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "send_to_story_skill_use_requires_confirmation": true,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "slug": "simple_story",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "LIVE",
  "id": 7981,
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1],
  "draft_id": 1234,
  "draft_name": "Updated Draft",
  "monitor_failures": false,
  "actions_with_monitoring": [],
  "recipients": [
    {
      "address": "test@example.com"
    }
  ]
}
```

### Sample request to enable send to story

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/stories/<<story_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "entry_action_id": "<<entry_action_id>>",
        "exit_action_ids": ["<<exit_action_id>>"],
        "send_to_story_access_source": "STS"
      }'
```

<!-- cspell:enable -->

### List

## Description

Retrieve a list of stories.

## Request

HTTP Method: **GET**

| Query Parameter       | Description                                                                                                                                                                                                                                          |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id               | **Optional** Return stories belonging to this team.                                                                                                                                                                                                  |
| folder_id             | **Optional** Return stories in this folder.                                                                                                                                                                                                          |
| per_page              | **Optional** Set the number of results returned per page.                                                                                                                                                                                            |
| page                  | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                                                                                                                                  |
| tags                  | **Optional** An array of tag names to filter by                                                                                                                                                                                                      |
| search                | **Optional** A string that searches against story name                                                                                                                                                                                               |
| filter                | **Optional** Filter by one of: `SEND_TO_STORY_ENABLED`, `HIGH_PRIORITY`, `API_ENABLED`, `PUBLISHED` (deprecated), `FAVORITE`, `CHANGE_CONTROL_ENABLED`, `CHANGE_CONTROL_DISABLED`, `ENABLED`, `DISABLED`, `LOCKED`                                   |
| order                 | **Optional** Order the results by one of: `NAME`, `NAME_DESC`, `RECENTLY_EDITED`, `LEAST_RECENTLY_EDITED`, `ACTION_COUNT_ASC`, `ACTION_COUNT_DESC`, `CONCURRENT_RUNS_COUNT_DESC`, `PENDING_ACTION_RUNS_COUNT_DESC`, `NOT_WORKING_ACTIONS_COUNT_DESC` |
| include_live_activity | **Optional** When set to `true`, includes live activity metrics like `pending_action_runs_count`, `concurrent_runs_count`, `tokens_used_percentage`, and `not_working_actions_count`. Defaults to `false`.                                           |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/stories?include_live_activity=true \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing stories in the Tines tenant.

### Field description

| Parameter                                     | Description                                                                                                                                                                            |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                                        |
| user_id                                       | ID of story creator.                                                                                                                                                                   |
| description                                   | A user-defined description of the story.                                                                                                                                               |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                                             |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                                                     |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                                          |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                                                   |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                                                       |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                                                      |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                                                     |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                                                     |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                                                       |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                                             |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                                            |
| team_id                                       | ID of team to which the story belongs.                                                                                                                                                 |
| tags                                          | An array of tags used to classify the story.                                                                                                                                           |
| guid                                          | Unique identifier of the story.                                                                                                                                                        |
| slug                                          | An underscored representation of the story name.                                                                                                                                       |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                                                   |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself.                         |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                                                    |
| mode                                          | The mode of the story. Only `LIVE` stories will be listed.                                                                                                                             |
| id                                            | The story ID.                                                                                                                                                                          |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                                               |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                                                |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                                                  |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                                        |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                                               |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on all actions in the story. This will be false when only some actions have monitoring enabled.                                 |
| actions_with_monitoring                       | List of action IDs with monitoring enabled.                                                                                                                                            |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                                           |
| pending_action_runs_count                     | Total number of pending action runs across all actions in the story. Only included when `include_live_activity=true` is set.                                                           |
| concurrent_runs_count                         | Number of concurrent story runs currently executing. Only included when `include_live_activity=true` is set.                                                                           |
| tokens_used_percentage                        | Percentage of token limit used by the story. Only included when `include_live_activity=true` is set.                                                                                   |
| not_working_actions_count                     | Number of actions in a "Not working" state in the story, meaning the action has not emitted an event since its last error log. Only included when `include_live_activity=true` is set. |

### Sample response

<!-- cspell:disable -->

```json
{
  "stories": [
    {
      "name": "Simple story",
      "user_id": 167,
      "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
      "keep_events_for": 604800,
      "disabled": false,
      "priority": false,
      "send_to_story_enabled": false,
      "send_to_story_access_source": "OFF",
      "send_to_story_access": null,
      "send_to_story_skill_use_requires_confirmation": true,
      "shared_team_slugs": [],
      "entry_agent_id": null,
      "exit_agents": [],
      "team_id": 1,
      "tags": ["Tag 1", "Tag 2"],
      "guid": "df1e838a18d20696120b41516497b017",
      "slug": "simple_story",
      "created_at": "2021-05-10T08:56:50Z",
      "updated_at": "2021-05-10T08:56:50Z",
      "edited_at": "2021-05-10T08:56:50Z",
      "mode": "LIVE",
      "id": 7981,
      "folder_id": 1,
      "published": true,
      "change_control_enabled": false,
      "locked": false,
      "owners": [1],
      "monitor_failures": false,
      "actions_with_monitoring": [],
      "recipients": [
        {
          "address": "test@example.com"
        }
      ],
      "pending_action_runs_count": 150,
      "concurrent_runs_count": 3,
      "tokens_used_percentage": 15.7
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/stories?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20, // Max: 500
    "pages": 1,
    "count": 1
  }
}
```

<!-- cspell:enable -->

### List pending actions

## Description

Retrieve a list of actions in a story that currently have pending action runs, ordered by the number of pending runs (highest first). This endpoint is useful for monitoring which actions in a story have a backlog of work to process.

## Request

HTTP Method: **GET**

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

| Query Parameter | Description                                                                                                                         |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| search          | **Optional** Search against action names.                                                                                           |
| story_mode      | **Optional** The mode (`TEST` or `LIVE`) of the story. Defaults to `LIVE`.                                                          |
| per_page        | **Optional** Set the number of results returned per page.                                                                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                 |
| draft_id        | **Optional** Return actions for a specific draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/pending_actions?story_mode=LIVE \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing the actions with pending runs for the specified story, ordered by pending run count (descending).

### Field description

| Parameter                 | Description                                    |
| ------------------------- | ---------------------------------------------- |
| id                        | Action ID.                                     |
| name                      | Name of the action.                            |
| type                      | Action type.                                   |
| pending_action_runs_count | Number of pending action runs for this action. |

### Sample response

```json
{
  "pending_actions": [
    {
      "id": 12345,
      "name": "Process webhook",
      "type": "Agents::EventTransformationAgent",
      "pending_action_runs_count": 150
    },
    {
      "id": 12346,
      "name": "Send notification",
      "type": "Agents::HTTPRequestAgent",
      "pending_action_runs_count": 45
    },
    {
      "id": 12347,
      "name": "Parse data",
      "type": "Agents::EventTransformationAgent",
      "pending_action_runs_count": 12
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/stories/<<story_id>>/pending_actions?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  }
}
```

### Delete

## Description

Delete a story.

## Request

HTTP Method: **DELETE**

| Parameter | Description                    |
| --------- | ------------------------------ |
| story_id  | The ID of the story to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/stories/<<story_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Batch delete

## Description

Delete many stories.

## Request

HTTP Method: **DELETE**

| Parameter | Description                       |
| --------- | --------------------------------- |
| ids       | The IDs of the stories to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/stories/batch \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "ids": [3, 4, 5]
      }'
```

## Response

A successful request will return an empty response with a `204` status code.

### Export

## Description

Export a story to a transferable story file.

## Request

HTTP Method: **GET**

| Path Parameter | Description                    |
| -------------- | ------------------------------ |
| story_id       | The ID of the story to export. |

| Query Parameter  | Description                                                                                                       |
| ---------------- | ----------------------------------------------------------------------------------------------------------------- |
| randomize_urls   | **Optional** Boolean flag indicating whether the story's page and webhook urls should be randomized.              |
| clear_recipients | **Optional** Boolean flag indicating whether the story's monitoring and email recipient fields should be cleared. |
| draft_id         | **Optional** The ID of the draft to export.                                                                       |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/export \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the story in exported form.

### Sample response

```json
{
  "schema_version": 18,
  "standard_lib_version": 41,
  "action_runtime_version": 6,
  "name": "Simple story",
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant. The alert will contain the type of alert (infection, ddos, credential stuffing, etc.) and details on any users affected. If the alert is related to an infection, based on the users job title, we will take a specific action.\r\n\r\nThe simple story is described in detail in the Tines Docs(https://hub.tines.com/docs/quickstart/simple-story/).\r\n\r\nUse the following URL command (replace $webhook-url with the webhook URL in the ''Summary\" tab of the 'Receive events' action) to send events to this story:\r\n\r\ncurl $webhook-url -X POST -H \"Content-Type: application/json\" -d '{\"event_name\":\"My first event\",\"type\":\"infection\",\"users\":[{\"name\":\"alice\",\"age\":25,\"country\":\"US\",\"job\":\"Engineer\"},{\"name\":\"bob\",\"age\":20,\"country\":\"UK\",\"job\":\"Student\"},{\"name\":\"carol\",\"age\":61,\"country\":\"Ireland\",\"job\":\"CEO\"}]}'",
  "guid": "8de58314250ff167127d6ae213711de9",
  "slug": "simple_story",
  "agents": [
    {
      "type": "Agents::TriggerAgent",
      "name": "User is ceo",
      "disabled": false,
      "description": null,
      "guid": "9fda86d826c2da6b1001d83de774e6cd",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "rules": [
          {
            "type": "regex",
            "value": "ceo",
            "path": "<<explode_users.user.job>>"
          }
        ]
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null
    },
    {
      "type": "Agents::EmailAgent",
      "name": "Notify by email",
      "disabled": false,
      "description": null,
      "guid": "2a1c3710c175566adbac7bdfcb8aa0af",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "recipients": "tinesdemouser@outlook.com",
        "subject": "Engineer in infected",
        "body": "Hello,<br/><br/>An alert has been detected that relates to an infected engineer. The user's details are shown below:<br/><b>Name:</b> <<explode_users.user.name>><br/><b>Age:</b> <<explode_users.user.age>><br/><b>Country:</b> <<explode_users.user.country>><br/><br/>Thanks!"
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null,
      "schedule": null
    },
    {
      "type": "Agents::WebhookAgent",
      "name": "Receive events",
      "disabled": false,
      "description": null,
      "guid": "2f1f4538338172e208f653d3a614afb6",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "secret": "201367c22bd83f4e79ac81aa9f9efb7c",
        "verbs": "post",
        "path": "faac546b6297d276b866af0487343601",
        "include_headers": "false"
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null
    },
    {
      "type": "Agents::TriggerAgent",
      "name": "Type is infection",
      "disabled": false,
      "description": null,
      "guid": "e606d66f945ba059655a672497d78170",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "rules": [
          {
            "type": "regex",
            "value": "infection",
            "path": "<<receive_events.type>>"
          }
        ]
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null
    },
    {
      "type": "Agents::EventTransformationAgent",
      "name": "Explode users",
      "disabled": false,
      "description": null,
      "guid": "51ce4f788c04cf81dc7eb7dda25f575b",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "mode": "explode",
        "path": "=receive_events.users",
        "to": "user"
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null,
      "schedule": null
    },
    {
      "type": "Agents::TriggerAgent",
      "name": "User is student",
      "disabled": false,
      "description": null,
      "guid": "845aa2aa857ffe8832856050abf7d994",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "rules": [
          {
            "type": "regex",
            "value": "student",
            "path": "<<explode_users.user.job>>"
          }
        ]
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null
    },
    {
      "type": "Agents::TriggerAgent",
      "name": "User is engineer",
      "disabled": false,
      "description": null,
      "guid": "bded45ec772c9f48984d39da6388ff56",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "rules": [
          {
            "type": "regex",
            "value": "engineer",
            "path": "<<explode_users.user.job>>"
          }
        ]
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null
    },
    {
      "type": "Agents::HTTPRequestAgent",
      "name": "Send a post request",
      "disabled": false,
      "description": null,
      "guid": "dbe0171f0253ebde721ddfb086c323a5",
      "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
      "options": {
        "url": "https://postman-echo.com/post",
        "content_type": "json",
        "method": "post",
        "payload": {
          "name": "<<explode_users.user.name>>",
          "age": "<<explode_users.user.age>>",
          "country": "<<explode_users.user.country>>",
          "job": "<<explode_users.user.job>>"
        },
        "log_error_on_status": []
      },
      "reporting": {
        "time_saved_value": 0,
        "time_saved_unit": "minutes"
      },
      "monitoring": {
        "monitor_all_events": false,
        "monitor_failures": false,
        "monitor_no_events_emitted": null
      },
      "template": {
        "created_from_template_guid": null,
        "created_from_template_version": null
      },
      "width": null,
      "schedule": null
    }
  ],
  "diagram_notes": [],
  "links": [
    {
      "source": 0,
      "receiver": 7
    },
    {
      "source": 2,
      "receiver": 3
    },
    {
      "source": 3,
      "receiver": 4
    },
    {
      "source": 4,
      "receiver": 6
    },
    {
      "source": 4,
      "receiver": 5
    },
    {
      "source": 4,
      "receiver": 0
    },
    {
      "source": 6,
      "receiver": 1
    }
  ],
  "diagram_layout": "{\"9fda86d826c2da6b1001d83de774e6cd\":[105,-195],\"2a1c3710c175566adbac7bdfcb8aa0af\":[345,-120],\"2f1f4538338172e208f653d3a614afb6\":[345,-435],\"e606d66f945ba059655a672497d78170\":[345,-360],\"51ce4f788c04cf81dc7eb7dda25f575b\":[345,-285],\"845aa2aa857ffe8832856050abf7d994\":[570,-195],\"bded45ec772c9f48984d39da6388ff56\":[345,-195],\"dbe0171f0253ebde721ddfb086c323a5\":[105,-120]}",
  "send_to_story_enabled": false,
  "send_to_story_skill_use_requires_confirmation": true,
  "entry_agent_guid": null,
  "exit_agent_guids": [],
  "exit_agent_guid": null,
  "api_entry_action_guids": [],
  "api_exit_action_guids": [],
  "keep_events_for": 86400,
  "reporting_status": true,
  "send_to_story_access": null,
  "story_library_metadata": {},
  "monitor_failures": false,
  "send_to_stories": [],
  "form": null,
  "synchronous_webhooks_enabled": false,
  "forms": [],
  "pages": [],
  "tags": [],
  "time_saved_unit": "minutes",
  "time_saved_value": 0,
  "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
  "integration_product": null,
  "integration_vendor": null,
  "exported_at": "2023-12-15T01:43:37Z",
  "integrations": []
}
```

### Import

## Description

Import a new story. Note that if the story data includes embedded substories these will not be imported.

## Request

HTTP Method: **POST**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| new_name        | The new name for the story.                                                                         |
| data            | JSON object representing the story in [exported form](/api/stories/export#sample-response)          |
| team_id         | ID of team to which the story should be added.                                                      |
| folder_id       | **Optional** ID of folder to which the story should be added.                                       |
| mode            | **Optional** Create a new story or update existing by Name (`new`, `versionReplace`) default: `new` |

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/stories/import \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "new_name": "Imported story",
        "team_id": 2,
        "folder_id": 1,
        "data": {
          "schema_version": 18,
          "standard_lib_version": 41,
          "action_runtime_version": 6,
          "name": "Simple story",
          "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant. The alert will contain the type of alert (infection, ddos, credential stuffing, etc.) and details on any users affected. If the alert is related to an infection, based on the users job title, we will take a specific action.\r\n\r\nThe simple story is described in detail in the Tines Docs(https://hub.tines.com/docs/quickstart/simple-story/).\r\n\r\nUse the following URL command (replace $webhook-url with the webhook URL in the ''Summary\" tab of the 'Receive events' action) to send events to this story:\r\n\r\ncurl $webhook-url -X POST -H \"Content-Type: application/json\" -d '{\"event_name\":\"My first event\",\"type\":\"infection\",\"users\":[{\"name\":\"alice\",\"age\":25,\"country\":\"US\",\"job\":\"Engineer\"},{\"name\":\"bob\",\"age\":20,\"country\":\"UK\",\"job\":\"Student\"},{\"name\":\"carol\",\"age\":61,\"country\":\"Ireland\",\"job\":\"CEO\"}]}'",
          "guid": "8de58314250ff167127d6ae213711de9",
          "slug": "simple_story",
          "agents": [
            {
              "type": "Agents::TriggerAgent",
              "name": "User is ceo",
              "disabled": false,
              "description": null,
              "guid": "9fda86d826c2da6b1001d83de774e6cd",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "rules": [
                  {
                    "type": "regex",
                    "value": "ceo",
                    "path": "<<explode_users.user.job>>"
                  }
                ]
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null
            },
            {
              "type": "Agents::EmailAgent",
              "name": "Notify by email",
              "disabled": false,
              "description": null,
              "guid": "2a1c3710c175566adbac7bdfcb8aa0af",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "recipients": "tinesdemouser@outlook.com",
                "subject": "Engineer in infected",
                "body": "Hello,<br/><br/>An alert has been detected that relates to an infected engineer. The user'\''s details are shown below:<br/><b>Name:</b> <<explode_users.user.name>><br/><b>Age:</b> <<explode_users.user.age>><br/><b>Country:</b> <<explode_users.user.country>><br/><br/>Thanks!"
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null,
              "schedule": null
            },
            {
              "type": "Agents::WebhookAgent",
              "name": "Receive events",
              "disabled": false,
              "description": null,
              "guid": "2f1f4538338172e208f653d3a614afb6",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "secret": "201367c22bd83f4e79ac81aa9f9efb7c",
                "verbs": "post",
                "path": "faac546b6297d276b866af0487343601",
                "include_headers": "false"
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null
            },
            {
              "type": "Agents::TriggerAgent",
              "name": "Type is infection",
              "disabled": false,
              "description": null,
              "guid": "e606d66f945ba059655a672497d78170",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "rules": [
                  {
                    "type": "regex",
                    "value": "infection",
                    "path": "<<receive_events.type>>"
                  }
                ]
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null
            },
            {
              "type": "Agents::EventTransformationAgent",
              "name": "Explode users",
              "disabled": false,
              "description": null,
              "guid": "51ce4f788c04cf81dc7eb7dda25f575b",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "mode": "explode",
                "path": "=receive_events.users",
                "to": "user"
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null,
              "schedule": null
            },
            {
              "type": "Agents::TriggerAgent",
              "name": "User is student",
              "disabled": false,
              "description": null,
              "guid": "845aa2aa857ffe8832856050abf7d994",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "rules": [
                  {
                    "type": "regex",
                    "value": "student",
                    "path": "<<explode_users.user.job>>"
                  }
                ]
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null
            },
            {
              "type": "Agents::TriggerAgent",
              "name": "User is engineer",
              "disabled": false,
              "description": null,
              "guid": "bded45ec772c9f48984d39da6388ff56",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "rules": [
                  {
                    "type": "regex",
                    "value": "engineer",
                    "path": "<<explode_users.user.job>>"
                  }
                ]
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null
            },
            {
              "type": "Agents::HTTPRequestAgent",
              "name": "Send a post request",
              "disabled": false,
              "description": null,
              "guid": "dbe0171f0253ebde721ddfb086c323a5",
              "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
              "options": {
                "url": "https://postman-echo.com/post",
                "content_type": "json",
                "method": "post",
                "payload": {
                  "name": "<<explode_users.user.name>>",
                  "age": "<<explode_users.user.age>>",
                  "country": "<<explode_users.user.country>>",
                  "job": "<<explode_users.user.job>>"
                },
                "log_error_on_status": []
              },
              "reporting": {
                "time_saved_value": 0,
                "time_saved_unit": "minutes"
              },
              "monitoring": {
                "monitor_all_events": false,
                "monitor_failures": false,
                "monitor_no_events_emitted": null
              },
              "template": {
                "created_from_template_guid": null,
                "created_from_template_version": null
              },
              "width": null,
              "schedule": null
            }
          ],
          "diagram_notes": [],
          "links": [
            {
              "source": 0,
              "receiver": 7
            },
            {
              "source": 2,
              "receiver": 3
            },
            {
              "source": 3,
              "receiver": 4
            },
            {
              "source": 4,
              "receiver": 6
            },
            {
              "source": 4,
              "receiver": 5
            },
            {
              "source": 4,
              "receiver": 0
            },
            {
              "source": 6,
              "receiver": 1
            }
          ],
          "diagram_layout": "{\"9fda86d826c2da6b1001d83de774e6cd\":[105,-195],\"2a1c3710c175566adbac7bdfcb8aa0af\":[345,-120],\"2f1f4538338172e208f653d3a614afb6\":[345,-435],\"e606d66f945ba059655a672497d78170\":[345,-360],\"51ce4f788c04cf81dc7eb7dda25f575b\":[345,-285],\"845aa2aa857ffe8832856050abf7d994\":[570,-195],\"bded45ec772c9f48984d39da6388ff56\":[345,-195],\"dbe0171f0253ebde721ddfb086c323a5\":[105,-120]}",
          "send_to_story_enabled": false,
          "send_to_story_skill_use_requires_confirmation": true,
          "entry_agent_guid": null,
          "exit_agent_guids": [],
          "exit_agent_guid": null,
          "api_entry_action_guids": [],
          "api_exit_action_guids": [],
          "keep_events_for": 86400,
          "reporting_status": true,
          "send_to_story_access": null,
          "story_library_metadata": {},
          "monitor_failures": false,
          "send_to_stories": [],
          "form": null,
          "synchronous_webhooks_enabled": false,
          "forms": [],
          "pages": [],
          "tags": [],
          "time_saved_unit": "minutes",
          "time_saved_value": 0,
          "origin_story_identifier": "cloud:72d8681fa16f17c9cb1b5d0118f96474:8de58314250ff167127d6ae213711de9",
          "integration_product": null,
          "integration_vendor": null,
          "exported_at": "2023-12-15T01:43:37Z",
          "integrations": []
          }
        }
      }'

```

## Response

A successful request will return a JSON object describing the newly created story

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                    |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name.                                                                                                               |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | The mode of the story. `LIVE` or `TEST`                                                                                                                        |
| id                                            | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on the story.                                                                                           |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                   |

### Sample response

<!-- cspell:disable -->

```json
{
  "name": "Simple story",
  "user_id": 167,
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "send_to_story_skill_use_requires_confirmation": true,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "slug": "simple_story",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "LIVE",
  "id": 7981,
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1],
  "monitor_failures": false,
  "recipients": [
    {
      "address": "test@example.com"
    }
  ]
}
```

<!-- cspell:enable -->

### Actions

#### Create

## Description

Use a HTTP POST request to create an action. Story or Group ID must be provided. Defaults to a draft called `test` if change control is enabled on the story and no draft_id is provided.

> **Note**: The Condition action was previously named 'Trigger' in Tines. To prevent workflow breakage, it is still referenced as `Agents::TriggerAgent` in API requests.

## Request

HTTP Method: **POST**

| Parameter                 | Description                                                                                                                                                                                                                                                                                             |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type                      | Type of action to create: <br />`Agents::EmailAgent`<br />`Agents::EventTransformationAgent`<br />`Agents::HTTPRequestAgent`<br />`Agents::IMAPAgent`<br />`Agents::LLMAgent`<br />`Agents::TriggerAgent`<br />`Agents::WebhookAgent`<br />`Agents::SendToStoryAgent`<br /> `Agents::GroupAgent` <br /> |
| name                      | Name of the action.                                                                                                                                                                                                                                                                                     |
| options                   | JSON Options block of the action.                                                                                                                                                                                                                                                                       |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                                                                                                                                                                                                             |
| story_id                  | **Optional** ID of story to which the action should be added.                                                                                                                                                                                                                                           |
| group_id                  | **Optional** ID of group to which the action should be added.                                                                                                                                                                                                                                           |
| description               | **Optional** A user-defined description of the action.                                                                                                                                                                                                                                                  |
| disabled                  | **Optional** Boolean flag indicating whether action is disabled. Defaults to `false`.                                                                                                                                                                                                                   |
| source_ids                | **Optional** Array of action IDs the action should receive emitted events from.                                                                                                                                                                                                                         |
| links_to_sources          | **Optional** Array of objects representing links to source actions. Mutually exclusive with source_ids field. Each object has source_id (required) and type (optional; "DEFAULT", "NO_MATCH", or "FAILURE")                                                                                             |
| receiver_ids              | **Optional** Array of action IDs the action should emit events to.                                                                                                                                                                                                                                      |
| links_to_receivers        | **Optional** Array of objects representing links to receiver actions. Mutually exclusive with receiver_ids field. Each object has receiver_id (required) and type (optional; "DEFAULT", "NO_MATCH", or "FAILURE")                                                                                       |
| schedule                  | **Optional** An object defining the cron schedule for the action.                                                                                                                                                                                                                                       |
| monitor_failures          | **Optional** Boolean flag indicating if a notification should be sent when this action fails.                                                                                                                                                                                                           |
| monitor_all_events        | **Optional** Boolean flag indicating if all events should be monitored.                                                                                                                                                                                                                                 |
| monitor_no_events_emitted | **Optional** Duration in seconds. If no events are emitted in this time, a notification will be sent.                                                                                                                                                                                                   |
| draft_id                  | **Optional** ID of the draft to which the action should be added. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints).                                                                                                                                                     |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/actions \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "type":"Agents::EventTransformationAgent",
        "name": "Extract email addresses and urls",
        "story_id":"{{.story_id}}",
        "source_ids": [100, 102],
        "receiver_ids":[],
        "position": {"x": 100, "y": 200},
        "options":{
          "mode": "extract",
          "matchers": [
            {
              "path": "{{.text}}",
              "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
              "to": "email_addresses"
            },
            {
              "path": "{{.text}}",
              "regexp": "https?:\\/\\/[\\S]+",
              "to": "urls"
            }
          ],
          "message": "This is an optional message"
        },
        "links_to_sources": [
          {
            "source_id": 10
          },
          {
            "source_id": 11,
            "type": "NO_MATCH"
          }
        ],
        "draft_id": 12345
      }'
```

## Response

A successful request will return a JSON object describing the created action.

### Field description

| Parameter                 | Description                                                                                                 |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| id                        | Action ID.                                                                                                  |
| type                      | Action type                                                                                                 |
| user_id                   | User ID of the action's owner.                                                                              |
| options                   | JSON Options block of the action.                                                                           |
| name                      | Name of the action.                                                                                         |
| description               | A user-defined description of the action.                                                                   |
| schedule                  | An object defining the cron schedule for the action.                                                        |
| blended_events_count      | Number of events action has emitted in both TEST and LIVE mode.                                             |
| logs_count                | Number of logs action has stored.                                                                           |
| last_check_at             | ISO 8601 Timestamp representing date and time of action's last scheduled run.                               |
| last_receive_at           | ISO 8601 Timestamp representing date and time of last event received.                                       |
| created_at                | ISO 8601 Timestamp representing creation date and time of action.                                           |
| updated_at                | ISO 8601 Timestamp representing last updated date and time of action.                                       |
| last_event_at             | ISO 8601 Timestamp representing date and time the last event was received. Updated at one-minute intervals. |
| last_error_log_at         | ISO 8601 Timestamp representing date and time of last error thrown by the action.                           |
| disabled                  | Boolean flag indicating whether action is disabled.                                                         |
| guid                      | Unique identifier of the action.                                                                            |
| group_id                  | ID of group to which the action belongs.                                                                    |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                 |
| story_id                  | ID of story to which the action belongs.                                                                    |
| story_mode                | Mode of the story to which the action belongs `LIVE` or `TEST`                                              |
| nested_group_id           | ID of group contained in the action.                                                                        |
| team_id                   | ID of team to which the action belongs.                                                                     |
| sources                   | An Array of Action IDs this action receives emitted events from.                                            |
| receivers                 | An Array of Action IDs this action emits events to.                                                         |
| monitor_failures          | Boolean flag indicating if a notification should be sent when this action fails.                            |
| monitor_all_events        | Boolean flag indicating if all events should be monitored.                                                  |
| monitor_no_events_emitted | Duration in seconds. If no events are emitted in this time, a notification will be sent.                    |
| time_saved_unit           | Unit of time corresponding to time saved value.                                                             |
| time_saved_value          | Number indicating the amount of time saved.                                                                 |
| page                      | An object with information on the associated page, if this is a page action                                 |
| action_memory_contents    | An object containing an array of the values an action may be holding in its memory.                         |
| slug                      | An underscored representation of the action's name                                                          |
| links_to_sources          | An array of links to source actions, including the source ID and the link type                              |
| links_to_receivers        | An array of links to receiver actions, including the receiver ID and the link type                          |
| draft_id                  | ID of the draft to which the action belongs.                                                                |
| draft_name                | Name of the draft to which the action belongs.                                                              |

### Sample response

```json
{
  "id": 73563,
  "type": "Agents::EventTransformationAgent",
  "user_id": 167,
  "options": {
    "mode": "extract",
    "matchers": [
      {
        "path": "",
        "regexp": "\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}\b",
        "to": "email_addresses"
      },
      {
        "path": "",
        "regexp": "https?://[S]+",
        "to": "urls"
      }
    ],
    "message": "This is an optional message"
  },
  "name": "Extract email addresses and urls",
  "description": null,
  "schedule": null,
  "blended_events_count": 0,
  "logs_count": 0,
  "last_check_at": null,
  "last_receive_at": null,
  "created_at": "2021-05-07T11:42:58Z",
  "updated_at": "2021-05-07T11:47:00Z",
  "last_event_at": null,
  "last_error_log_at": null,
  "disabled": false,
  "guid": "f3fe6f8e167c9db42e64eaef8e5d2f0c",
  "group_id": null,
  "position": {
    "x": 105,
    "y": 195
  },
  "receivers": [70118],
  "sources": [70116],
  "team_id": 335,
  "monitor_failures": false,
  "monitor_all_events": false,
  "monitor_no_events_emitted": null,
  "time_saved_unit": "minutes",
  "time_saved_value": 0,
  "page": null,
  "story_id": 807,
  "story_mode": "LIVE",
  "nested_group_id": null,
  "links_to_sources": {
    "source_id": 70116,
    "link_type": "DEFAULT"
  },
  "links_to_receivers": {
    "receiver_id": 70118,
    "link_type": "NO_MATCH"
  },
  "draft_id": 12345,
  "draft_name": "Initial Draft"
}
```

#### Get

## Description

Retrieve details of a specific action.

## Request

HTTP Method: **GET**

| Path Parameter | Description      |
| -------------- | ---------------- |
| action_id      | ID of the action |

| Query Parameter       | Description                                                                                                            |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| include_live_activity | **Optional** When set to `true`, includes live activity metrics like `pending_action_runs_count`. Defaults to `false`. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/actions/<<action_id>>?include_live_activity=true \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the requested action.

### Field description

| Parameter                 | Description                                                                                                                                                                                             |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                        | Action ID.                                                                                                                                                                                              |
| type                      | Action type                                                                                                                                                                                             |
| user_id                   | User ID of the action's owner.                                                                                                                                                                          |
| options                   | JSON Options block of the action.                                                                                                                                                                       |
| name                      | Name of the action.                                                                                                                                                                                     |
| description               | A user-defined description of the action.                                                                                                                                                               |
| schedule                  | An object defining the cron schedule for the action.                                                                                                                                                    |
| blended_events_count      | Number of events action has emitted in both TEST and LIVE mode.                                                                                                                                         |
| logs_count                | Number of logs action has stored.                                                                                                                                                                       |
| last_check_at             | ISO 8601 Timestamp representing date and time of action's last scheduled run.                                                                                                                           |
| last_receive_at           | ISO 8601 Timestamp representing date and time of last event received.                                                                                                                                   |
| created_at                | ISO 8601 Timestamp representing creation date and time of action.                                                                                                                                       |
| updated_at                | ISO 8601 Timestamp representing last updated date and time of action.                                                                                                                                   |
| last_event_at             | ISO 8601 Timestamp representing date and time the last event was received. Updated at one-minute intervals.                                                                                             |
| last_error_log_at         | ISO 8601 Timestamp representing date and time of last error thrown by the action.                                                                                                                       |
| disabled                  | Boolean flag indicating whether action is disabled.                                                                                                                                                     |
| guid                      | Unique identifier of the action.                                                                                                                                                                        |
| group_id                  | ID of group to which the action belongs.                                                                                                                                                                |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                                                                                                             |
| story_id                  | ID of story to which the action belongs.                                                                                                                                                                |
| story_mode                | Mode of the story to which the action belongs `LIVE` or `TEST`                                                                                                                                          |
| nested_group_id           | ID of group contained in the action.                                                                                                                                                                    |
| team_id                   | ID of team to which the action belongs.                                                                                                                                                                 |
| sources                   | An Array of Action IDs this action receives emitted events from.                                                                                                                                        |
| receivers                 | An Array of Action IDs this action emits events to.                                                                                                                                                     |
| monitor_failures          | Boolean flag indicating if a notification should be sent when this action fails.                                                                                                                        |
| monitor_all_events        | Boolean flag indicating if all events should be monitored.                                                                                                                                              |
| monitor_no_events_emitted | Duration in seconds. If no events are emitted in this time, a notification will be sent.                                                                                                                |
| time_saved_unit           | Unit of time corresponding to time saved value.                                                                                                                                                         |
| time_saved_value          | Number indicating the amount of time saved.                                                                                                                                                             |
| page                      | An object with information on the associated page, if this is a page action                                                                                                                             |
| action_memory_contents    | An object containing an array of the values an action may be holding in its memory.                                                                                                                     |
| slug                      | An underscored representation of the action's name                                                                                                                                                      |
| links_to_sources          | An array of links to source actions, including the source ID and the link type                                                                                                                          |
| links_to_receivers        | An array of links to receiver actions, including the receiver ID and the link type                                                                                                                      |
| story_run_save_setting    | An object containing the story run save settings for this action. Only present for Receive Email and Webhook agents. [About story runs](https://www.tines.com/docs/stories/story-runs/#save-story-runs) |
| pending_action_runs_count | Number of pending action runs for this action. Only included when `include_live_activity=true` is set.                                                                                                  |

### Sample response

```json
{
  "id": 73563,
  "type": "Agents::EventTransformationAgent",
  "user_id": 167,
  "options": {
    "mode": "deduplicate",
    "path": "{{.propagation_webhook.password}}",
    "lookback": "1"
  },
  "name": "Deduplicate",
  "schedule": [
    {
      "cron": "*/1 * * * *",
      "timezone": "Europe/Dublin"
    }
  ],
  "blended_events_count": 2,
  "logs_count": 4,
  "last_check_at": "2021-05-07T11:46:50Z",
  "last_receive_at": "2021-05-07T11:46:50Z",
  "created_at": "2021-05-07T11:42:58Z",
  "updated_at": "2021-05-07T11:47:00Z",
  "last_event_at": "2021-05-07T11:47:00Z",
  "last_error_log_at": null,
  "disabled": false,
  "guid": "f3fe6f8e167c9db42e64eaef8e5d2f0c",
  "group_id": null,
  "position": {
    "x": -945,
    "y": 450
  },
  "receivers": [70118],
  "sources": [70116],
  "story_id": 807,
  "story_mode": "LIVE",
  "team_id": 335,
  "monitor_failures": true,
  "monitor_all_events": true,
  "monitor_no_events_emitted": 86400,
  "time_saved_unit": "minutes",
  "time_saved_value": 10,
  "page": null,
  "action_memory_contents": {
    "values": []
  },
  "story_id": 807,
  "story_mode": "LIVE",
  "nested_group_id": null,
  "links_to_sources": {
    "source_id": 70116,
    "link_type": "DEFAULT"
  },
  "links_to_receivers": {
    "receiver_id": 70118,
    "link_type": "NO_MATCH"
  },
  "pending_action_runs_count": 42
}
```

#### Update

## Description

Update an action.

## Request

HTTP Method: **PUT**

| Parameter                 | Description                                                                                                                                                                                                       |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| action_id                 | ID of the action                                                                                                                                                                                                  |
| name                      | **Optional** Name of the action.                                                                                                                                                                                  |
| description               | **Optional** A user-defined description of the action.                                                                                                                                                            |
| options                   | **Optional** JSON Options block of the action.                                                                                                                                                                    |
| position                  | **Optional** An object describing the XY coordinates of the action on the story diagram.                                                                                                                          |
| source_ids                | **Optional** Array of action IDs the action should receive emitted events from.                                                                                                                                   |
| links_to_sources          | **Optional** Array of objects representing links to source actions. Mutually exclusive with source_ids field. Each object has source_id (required) and type (optional, "DEFAULT" or "NO_MATCH")                   |
| receiver_ids              | **Optional** Array of action IDs the action should emit events to.                                                                                                                                                |
| links_to_receivers        | **Optional** Array of objects representing links to receiver actions. Mutually exclusive with receiver_ids field. Each object has receiver_id (required) and type (optional, "DEFAULT" or "NO_MATCH")             |
| schedule                  | **Optional** An object defining the cron schedule for the action.                                                                                                                                                 |
| disabled                  | **Optional** Boolean flag indicating whether action is disabled.                                                                                                                                                  |
| monitor_failures          | **Optional** Boolean flag indicating if a notification should be sent when this action fails. **NOTE** You must include the monitor_all_events parameter if updating this value                                   |
| monitor_all_events        | **Optional** Boolean flag indicating if all events should be monitored. **NOTE** You must include the monitor_failures parameter if updating this value                                                           |
| monitor_no_events_emitted | **Optional** Duration in seconds. If no events are emitted in this time, a notification will be sent. **NOTE** You must include both the monitor_failures and monitor_all_event parameters if updating this value |
| num_runs_to_save          | **Optional** Number of story runs to save for this action. Only present for Receive Email and Webhook agents. [About story runs](https://www.tines.com/docs/stories/story-runs/#save-story-runs)                  |

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/actions/<<action_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "New action name"
      }'
```

### Add links with types

```bash
curl -v -X PUT \
  "https://<tenant-domain>/api/v1/actions/<<source_action_id>>" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "links_to_receivers": [
          {
            "receiver_id": "<<receiver_action_id>>",
            "type": "NO_MATCH"
          }
        ]
      }'
```

## Response

A successful request will return a JSON object describing the updated action.

### Field description

| Parameter                 | Description                                                                                                                                                                                             |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                        | Action ID.                                                                                                                                                                                              |
| type                      | Action type                                                                                                                                                                                             |
| user_id                   | User ID of the action's owner.                                                                                                                                                                          |
| options                   | JSON Options block of the action.                                                                                                                                                                       |
| name                      | Name of the action.                                                                                                                                                                                     |
| description               | A user-defined description of the action.                                                                                                                                                               |
| schedule                  | An object defining the cron schedule for the action.                                                                                                                                                    |
| blended_events_count      | Number of events action has emitted in both TEST and LIVE mode.                                                                                                                                         |
| logs_count                | Number of logs action has stored.                                                                                                                                                                       |
| last_check_at             | ISO 8601 Timestamp representing date and time of action's last scheduled run.                                                                                                                           |
| last_receive_at           | ISO 8601 Timestamp representing date and time of last event received.                                                                                                                                   |
| created_at                | ISO 8601 Timestamp representing creation date and time of action.                                                                                                                                       |
| updated_at                | ISO 8601 Timestamp representing last updated date and time of action.                                                                                                                                   |
| last_event_at             | ISO 8601 Timestamp representing date and time the last event was received. Updated at one-minute intervals.                                                                                             |
| last_error_log_at         | ISO 8601 Timestamp representing date and time of last error thrown by the action.                                                                                                                       |
| disabled                  | Boolean flag indicating whether action is disabled.                                                                                                                                                     |
| guid                      | Unique identifier of the action.                                                                                                                                                                        |
| group_id                  | ID of group to which the action belongs.                                                                                                                                                                |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                                                                                                             |
| story_id                  | ID of story to which the action belongs.                                                                                                                                                                |
| story_mode                | Mode of the story to which the action belongs `LIVE` or `TEST`                                                                                                                                          |
| nested_group_id           | ID of group contained in the action.                                                                                                                                                                    |
| team_id                   | ID of team to which the action belongs.                                                                                                                                                                 |
| sources                   | An Array of Action IDs this action receives emitted events from.                                                                                                                                        |
| receivers                 | An Array of Action IDs this action emits events to.                                                                                                                                                     |
| monitor_failures          | Boolean flag indicating if a notification should be sent when this action fails.                                                                                                                        |
| monitor_all_events        | Boolean flag indicating if all events should be monitored.                                                                                                                                              |
| monitor_no_events_emitted | Duration in seconds. If no events are emitted in this time, a notification will be sent.                                                                                                                |
| time_saved_unit           | Unit of time corresponding to time saved value.                                                                                                                                                         |
| time_saved_value          | Number indicating the amount of time saved.                                                                                                                                                             |
| page                      | An object with information on the associated page, if this is a page action                                                                                                                             |
| action_memory_contents    | An object containing an array of the values an action may be holding in its memory.                                                                                                                     |
| slug                      | An underscored representation of the action's name                                                                                                                                                      |
| links_to_sources          | An array of links to source actions, including the source ID and the link type                                                                                                                          |
| links_to_receivers        | An array of links to receiver actions, including the receiver ID and the link type                                                                                                                      |
| story_run_save_setting    | An object containing the story run save settings for this action. Only present for Receive Email and Webhook agents. [About story runs](https://www.tines.com/docs/stories/story-runs/#save-story-runs) |

### Sample response

```json
{
  "id": 73563,
  "user_id": 167,
  "type": "Agents::EventTransformationAgent",
  "options": {
    "mode": "deduplicate",
    "path": "{{.propagation_webhook.password}}",
    "lookback": "1"
  },
  "name": "New action name",
  "description": null,
  "schedule": [
    {
      "cron": "*/1 * * * *",
      "timezone": "Europe/Dublin"
    }
  ],
  "blended_events_count": 2,
  "logs_count": 4,
  "last_check_at": "2021-05-07T11:46:50Z",
  "last_receive_at": "2021-05-07T11:46:50Z",
  "created_at": "2021-05-07T11:42:58Z",
  "updated_at": "2021-05-07T11:47:00Z",
  "last_event_at": "2021-05-07T11:47:00Z",
  "last_error_log_at": null,
  "disabled": false,
  "guid": "f3fe6f8e167c9db42e64eaef8e5d2f0c",
  "group_id": null,
  "position": {
    "x": -945,
    "y": 450
  },
  "receivers": [70118],
  "sources": [70116],
  "story_id": 807,
  "story_mode": "LIVE",
  "team_id": 335,
  "monitor_failures": true,
  "monitor_all_events": true,
  "monitor_no_events_emitted": 86400,
  "time_saved_unit": "minutes",
  "time_saved_value": 10,
  "page": null,
  "story_id": 807,
  "story_mode": "LIVE",
  "nested_group_id": null,
  "links_to_sources": {
    "source_id": 70116,
    "link_type": "DEFAULT"
  },
  "links_to_receivers": {
    "receiver_id": 70118,
    "link_type": "NO_MATCH"
  }
}
```

#### List

## Description

Retrieve a list of actions available in the Tines tenant.

## Request

HTTP Method: **GET**

| Parameter             | Description                                                                                                                                                                                                                                                                                                                    |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| story_id              | **Optional** List actions for the given story                                                                                                                                                                                                                                                                                  |
| story_mode            | **Optional** List actions for the given story mode (must be used with story_id)                                                                                                                                                                                                                                                |
| team_id               | **Optional** List actions for the given team                                                                                                                                                                                                                                                                                   |
| group_id              | **Optional** List actions for the given group                                                                                                                                                                                                                                                                                  |
| action_type           | **Optional** Filter actions by the given type: <br />`Agents::EmailAgent`<br />`Agents::EventTransformationAgent`<br />`Agents::HTTPRequestAgent`<br />`Agents::IMAPAgent`<br />`Agents::TriggerAgent`<br />`Agents::WebhookAgent`<br />`Agents::SendToStoryAgent`<br /> `Agents::GroupAgent` <br /> `Agents::LLMAgent` <br /> |
| per_page              | **Optional** Set the number of results returned per page.                                                                                                                                                                                                                                                                      |
| page                  | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                                                                                                                                                                                                            |
| draft_id              | **Optional** List actions for the given draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints).                                                                                                                                                                                               |
| include_live_activity | **Optional** When set to `true`, includes live activity metrics like `pending_action_runs_count`. Defaults to `false`.                                                                                                                                                                                                         |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/actions?include_live_activity=true \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing actions in the Tines tenant.

### Field description

| Parameter                 | Description                                                                                                                                                                                             |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                        | Action ID.                                                                                                                                                                                              |
| type                      | Action type                                                                                                                                                                                             |
| user_id                   | User ID of the action's owner.                                                                                                                                                                          |
| options                   | JSON Options block of the action.                                                                                                                                                                       |
| name                      | Name of the action.                                                                                                                                                                                     |
| description               | A user-defined description of the action.                                                                                                                                                               |
| schedule                  | An object defining the cron schedule for the action.                                                                                                                                                    |
| blended_events_count      | Number of events action has emitted in both TEST and LIVE mode.                                                                                                                                         |
| logs_count                | Number of logs action has stored.                                                                                                                                                                       |
| last_check_at             | ISO 8601 Timestamp representing date and time of action's last scheduled run.                                                                                                                           |
| last_receive_at           | ISO 8601 Timestamp representing date and time of last event received.                                                                                                                                   |
| created_at                | ISO 8601 Timestamp representing creation date and time of action.                                                                                                                                       |
| updated_at                | ISO 8601 Timestamp representing last updated date and time of action.                                                                                                                                   |
| last_event_at             | ISO 8601 Timestamp representing date and time the last event was received. Updated at one-minute intervals.                                                                                             |
| last_error_log_at         | ISO 8601 Timestamp representing date and time of last error thrown by the action.                                                                                                                       |
| disabled                  | Boolean flag indicating whether action is disabled.                                                                                                                                                     |
| guid                      | Unique identifier of the action.                                                                                                                                                                        |
| group_id                  | ID of group to which the action belongs.                                                                                                                                                                |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                                                                                                             |
| story_id                  | ID of story to which the action belongs.                                                                                                                                                                |
| story_mode                | Mode of the story to which the action belongs `LIVE` or `TEST`                                                                                                                                          |
| nested_group_id           | ID of group contained in the action.                                                                                                                                                                    |
| team_id                   | ID of team to which the action belongs.                                                                                                                                                                 |
| sources                   | An Array of Action IDs this action receives emitted events from.                                                                                                                                        |
| receivers                 | An Array of Action IDs this action emits events to.                                                                                                                                                     |
| monitor_failures          | Boolean flag indicating if a notification should be sent when this action fails.                                                                                                                        |
| monitor_all_events        | Boolean flag indicating if all events should be monitored.                                                                                                                                              |
| monitor_no_events_emitted | Duration in seconds. If no events are emitted in this time, a notification will be sent.                                                                                                                |
| time_saved_unit           | Unit of time corresponding to time saved value.                                                                                                                                                         |
| time_saved_value          | Number indicating the amount of time saved.                                                                                                                                                             |
| page                      | An object with information on the associated page, if this is a page action                                                                                                                             |
| action_memory_contents    | An object containing an array of the values an action may be holding in its memory.                                                                                                                     |
| slug                      | An underscored representation of the action's name                                                                                                                                                      |
| links_to_sources          | An array of links to source actions, including the source ID and the link type                                                                                                                          |
| links_to_receivers        | An array of links to receiver actions, including the receiver ID and the link type                                                                                                                      |
| draft_id                  | ID of the draft associated with the action.                                                                                                                                                             |
| draft_name                | Name of the draft associated with the action.                                                                                                                                                           |
| story_run_save_setting    | An object containing the story run save settings for this action. Only present for Receive Email and Webhook agents. [About story runs](https://www.tines.com/docs/stories/story-runs/#save-story-runs) |
| pending_action_runs_count | Number of pending action runs for this action. Only included when `include_live_activity=true` is set.                                                                                                  |

### Sample response

```json
{
  "agents": [
    {
      "id": 73563,
      "type": "Agents::EventTransformationAgent",
      "user_id": 167,
      "options": {
        "mode": "deduplicate",
        "path": "{{.propagation_webhook.password}}",
        "lookback": "1"
      },
      "name": "Deduplicate",
      "schedule": [
        {
          "cron": "*/1 * * * *",
          "timezone": "Europe/Dublin"
        }
      ],
      "blended_events_count": 2,
      "logs_count": 4,
      "last_check_at": "2021-05-07T11:46:50Z",
      "last_receive_at": "2021-05-07T11:46:50Z",
      "created_at": "2021-05-07T11:42:58Z",
      "updated_at": "2021-05-07T11:47:00Z",
      "last_event_at": "2021-05-07T11:47:00Z",
      "last_error_log_at": null,
      "disabled": false,
      "guid": "f3fe6f8e167c9db42e64eaef8e5d2f0c",
      "group_id": null,
      "position": {
        "x": -945,
        "y": 450
      },
      "receivers": [70118],
      "sources": [70116],
      "story_id": 807,
      "story_mode": "LIVE",
      "team_id": 335,
      "monitor_failures": true,
      "monitor_all_events": true,
      "monitor_no_events_emitted": 86400,
      "time_saved_unit": "minutes",
      "time_saved_value": 10,
      "page": null,
      "story_id": 807,
      "story_mode": "LIVE",
      "nested_group_id": null,
      "links_to_sources": {
        "source_id": 70116,
        "link_type": "DEFAULT"
      },
      "links_to_receivers": {
        "receiver_id": 70118,
        "link_type": "NO_MATCH"
      },
      "draft_id": 12345,
      "draft_name": "Initial Draft",
      "pending_action_runs_count": 42
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/actions?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### List events

## Description

Retrieve a list of events emitted by a specified action.

## Request

HTTP Method: **GET**

| Parameter                  | Description                                                                                         |
| -------------------------- | --------------------------------------------------------------------------------------------------- |
| action_id                  | ID of the action                                                                                    |
| since_id                   | **Optional** Only retrieve events after this ID.                                                    |
| until_id                   | **Optional** Only retrieve events until (and including) this ID.                                    |
| show_reemitted_events_only | **Optional** Only retrieve events that have been re-emitted. Boolean, defaults to `false`.          |
| per_page                   | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 500.           |
| page                       | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/actions/<<action_id>>/events \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing events emitted by the specified action.

### Field description

| Parameter           | Description                                                                                                        |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| id                  | Event ID.                                                                                                          |
| agent_id            | ID of action that emitted the event.                                                                               |
| payload             | JSON representation of the event payload.                                                                          |
| created_at          | ISO 8601 Timestamp representing date and time the event was emitted.                                               |
| updated_at          | ISO 8601 Timestamp representing date and time the event was last updated. Will always be the same as 'created_at'. |
| story_run_guid      | Unique identifier of that event's story run.                                                                       |
| previous_events_ids | IDs of the upstream action's events in that event's story run.                                                     |
| re_emitted          | Boolean flag indicating whether this event has been re-emitted.                                                    |

### Sample response

<!-- cspell:disable -->

```json
{
  "events": [
    {
      "id": 79240,
      "agent_id": 21,
      "payload": {
        "propagation_webhook": {
          "username": "joe.bloggs",
          "age": 99
        },
        "delay_http_request": {
          "body": {
            "delay": "5"
          },
          "headers": {
            "access-control-allow-credentials": "",
            "access-control-allow-headers": "",
            "access-control-allow-methods": "",
            "access-control-allow-origin": "",
            "access-control-expose-headers": "",
            "content-type": "application/json; charset=utf-8",
            "date": "Tue, 03 Apr 2018 13:37:24 GMT",
            "etag": "W/\"d-N6WkUL2Jgn7fCGyPWbbptw\"",
            "server": "nginx",
            "set-cookie": "sails.sid=s%3A6Hr22AAbEOurnGiqRo3; Path=/; HttpOnly",
            "vary": "Accept-Encoding",
            "content-length": "13",
            "connection": "Close"
          },
          "status": 200
        }
      },
      "created_at": "2018-04-03T13:37:24.479Z",
      "updated_at": "2018-04-03T13:37:24.479Z",
      "story_run_guid": "d59cd024-38b2-4287-b298-a8ee8dc86863",
      "previous_events_ids": [1785155927],
      "re_emitted": false
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/agents/79240/events?page=1&per_page=20&",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

### Large payloads

Please note that a response payload size exceeding 200MB will result in a 422 error.

<!-- cspell:enable -->

#### List logs

## Description

List all logs emitted by a specific action.

## Request

HTTP Method: **GET**

| Path Parameter | Description      |
| -------------- | ---------------- |
| action_id      | ID of the action |

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| level           | **Optional** Filter logs by a given severity level (warning = 2, info = 3, error = 4).              |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/actions/<<action_id>>/logs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object listing the action logs.

### Field description

| Parameter     | Description                                                               |
| ------------- | ------------------------------------------------------------------------- |
| id            | Action log ID.                                                            |
| level         | The severity level of the log (warning = 2, info = 3, error = 4).         |
| message       | The log message.                                                          |
| created_at    | ISO 8601 Timestamp representing creation date and time of the action log. |
| inbound_event | The inbound event for the action run.                                     |

### Sample response

```json

{
"action_logs": [
  {
    "id": 733,
    "level": 3,
    "message": "Sending request to https://<tenant-domain>/api/v1/stories/ with options:
      {
        \"url\": \"https://<tenant-domain>/api/v1/stories/\",
        \"params\": {},
        \"body\": null,
        \"headers\": {
          \"content-type\": \"application/json\",
          \"Authorization\": \"Bearer ****************\",
          \"User-Agent\": \"Tines (Advanced Security Automation; tines.com)\",
          \"Content-Type\": null
        },
        \"method\": \"get\"
      }",
    "created_at": "2023-12-20T14:41:15Z",
    "inbound_event": null
  },
],
"meta": {
  "current_page": "https://<tenant-domain>/api/v1/actions/<<action_id>>/logs?per_page=20&page=1",
  "previous_page": null,
  "next_page": null,
  "next_page_number": null,
  "per_page": 20,
  "pages": 1,
  "count":1
  }
}

```

#### Delete

## Description

Delete a specific action.

## Request

HTTP Method: **DELETE**

| Parameter | Description      |
| --------- | ---------------- |
| action_id | ID of the action |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/actions/<<action_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete events

## Description

Delete all events emitted by a specific action.

## Request

HTTP Method: **DELETE**

| Parameter      | Description                                                                                       |
| -------------- | ------------------------------------------------------------------------------------------------- |
| action_id      | ID of the action                                                                                  |
| async_deletion | Boolean flag indicating if the deletion should be asynchronous by sending it to a background job. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/actions/<<action_id>>/remove_events \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the action with deleted events.

### Field description

| Parameter                 | Description                                                                                                 |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| id                        | Action ID.                                                                                                  |
| type                      | Action type                                                                                                 |
| user_id                   | User ID of the action's owner.                                                                              |
| options                   | JSON Options block of the action.                                                                           |
| name                      | Name of the action.                                                                                         |
| description               | A user-defined description of the action.                                                                   |
| schedule                  | An object defining the cron schedule for the action.                                                        |
| blended_events_count      | Number of events action has emitted in both TEST and LIVE mode.                                             |
| logs_count                | Number of logs action has stored.                                                                           |
| last_check_at             | ISO 8601 Timestamp representing date and time of action's last scheduled run.                               |
| last_receive_at           | ISO 8601 Timestamp representing date and time of last event received.                                       |
| created_at                | ISO 8601 Timestamp representing creation date and time of action.                                           |
| updated_at                | ISO 8601 Timestamp representing last updated date and time of action.                                       |
| last_event_at             | ISO 8601 Timestamp representing date and time the last event was received. Updated at one-minute intervals. |
| last_error_log_at         | ISO 8601 Timestamp representing date and time of last error thrown by the action.                           |
| disabled                  | Boolean flag indicating whether action is disabled.                                                         |
| guid                      | Unique identifier of the action.                                                                            |
| group_id                  | ID of group to which the action belongs.                                                                    |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                 |
| story_id                  | ID of story to which the action belongs.                                                                    |
| story_mode                | Mode of the story to which the action belongs `LIVE` or `TEST`                                              |
| nested_group_id           | ID of group contained in the action.                                                                        |
| team_id                   | ID of team to which the action belongs.                                                                     |
| sources                   | An Array of Action IDs this action receives emitted events from.                                            |
| receivers                 | An Array of Action IDs this action emits events to.                                                         |
| monitor_failures          | Boolean flag indicating if a notification should be sent when this action fails.                            |
| monitor_all_events        | Boolean flag indicating if all events should be monitored.                                                  |
| monitor_no_events_emitted | Duration in seconds. If no events are emitted in this time, a notification will be sent.                    |
| time_saved_unit           | Unit of time corresponding to time saved value.                                                             |
| time_saved_value          | Number indicating the amount of time saved.                                                                 |
| page                      | An object with information on the associated page, if this is a page action                                 |
| action_memory_contents    | An object containing an array of the values an action may be holding in its memory.                         |
| slug                      | An underscored representation of the action's name                                                          |
| links_to_sources          | An array of links to source actions, including the source ID and the link type                              |
| links_to_receivers        | An array of links to receiver actions, including the receiver ID and the link type                          |

### Sample response

```json
{
  "id": 73563,
  "type": "Agents::EventTransformationAgent",
  "user_id": 167,
  "options": {
    "mode": "deduplicate",
    "path": "<<deduplicate_path.alert>>",
    "lookback": "10"
  },
  "name": "Deduplicate alerts",
  "description": null,
  "schedule": null,
  "blended_events_count": 0,
  "logs_count": 2,
  "last_check_at": null,
  "last_receive_at": "2023-12-11T20:13:57Z",
  "created_at": "2021-05-07T11:42:58Z",
  "updated_at": "2021-05-07T11:47:00Z",
  "last_event_at": "2023-12-11T20:13:52Z",
  "last_error_log_at": null,
  "disabled": false,
  "guid": "f3fe6f8e167c9db42e64eaef8e5d2f0c",
  "group_id": null,
  "position": {
    "x": 105,
    "y": 195
  },
  "receivers": [70118],
  "sources": [70116],
  "team_id": 335,
  "monitor_failures": false,
  "monitor_all_events": false,
  "monitor_no_events_emitted": null,
  "time_saved_unit": "minutes",
  "time_saved_value": 0,
  "page": null,
  "story_id": 807,
  "story_mode": "LIVE",
  "nested_group_id": null,
  "links_to_sources": {
    "source_id": 70116,
    "link_type": "DEFAULT"
  },
  "links_to_receivers": {
    "receiver_id": 70118,
    "link_type": "NO_MATCH"
  }
}
```

#### Delete logs

## Description

Delete all logs emitted by a specific action.

## Request

HTTP Method: **DELETE**

| Parameter | Description      |
| --------- | ---------------- |
| action_id | ID of the action |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/actions/<<action_id>>/remove_logs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the action with deleted logs.

### Field description

| Parameter                 | Description                                                                                                 |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| id                        | Action ID.                                                                                                  |
| type                      | Action type                                                                                                 |
| user_id                   | User ID of the action's owner.                                                                              |
| options                   | JSON Options block of the action.                                                                           |
| name                      | Name of the action.                                                                                         |
| description               | A user-defined description of the action.                                                                   |
| schedule                  | An object defining the cron schedule for the action.                                                        |
| blended_events_count      | Number of events action has emitted in both TEST and LIVE mode.                                             |
| logs_count                | Number of logs action has stored.                                                                           |
| last_check_at             | ISO 8601 Timestamp representing date and time of action's last scheduled run.                               |
| last_receive_at           | ISO 8601 Timestamp representing date and time of last event received.                                       |
| created_at                | ISO 8601 Timestamp representing creation date and time of action.                                           |
| updated_at                | ISO 8601 Timestamp representing last updated date and time of action.                                       |
| last_event_at             | ISO 8601 Timestamp representing date and time the last event was received. Updated at one-minute intervals. |
| last_error_log_at         | ISO 8601 Timestamp representing date and time of last error thrown by the action.                           |
| disabled                  | Boolean flag indicating whether action is disabled.                                                         |
| guid                      | Unique identifier of the action.                                                                            |
| group_id                  | ID of group to which the action belongs.                                                                    |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                 |
| story_id                  | ID of story to which the action belongs.                                                                    |
| story_mode                | Mode of the story to which the action belongs `LIVE` or `TEST`                                              |
| nested_group_id           | ID of group contained in the action.                                                                        |
| team_id                   | ID of team to which the action belongs.                                                                     |
| sources                   | An Array of Action IDs this action receives emitted events from.                                            |
| receivers                 | An Array of Action IDs this action emits events to.                                                         |
| monitor_failures          | Boolean flag indicating if a notification should be sent when this action fails.                            |
| monitor_all_events        | Boolean flag indicating if all events should be monitored.                                                  |
| monitor_no_events_emitted | Duration in seconds. If no events are emitted in this time, a notification will be sent.                    |
| time_saved_unit           | Unit of time corresponding to time saved value.                                                             |
| time_saved_value          | Number indicating the amount of time saved.                                                                 |
| page                      | An object with information on the associated page, if this is a page action                                 |
| action_memory_contents    | An object containing an array of the values an action may be holding in its memory.                         |
| slug                      | An underscored representation of the action's name                                                          |
| links_to_sources          | An array of links to source actions, including the source ID and the link type                              |
| links_to_receivers        | An array of links to receiver actions, including the receiver ID and the link type                          |

### Sample response

```json
{
  "id": 73563,
  "type": "Agents::EventTransformationAgent",
  "user_id": 167,
  "options": {
    "mode": "deduplicate",
    "path": "<<deduplicate_path.alert>>",
    "lookback": "10"
  },
  "name": "Deduplicate alerts",
  "description": null,
  "schedule": null,
  "blended_events_count": 1,
  "logs_count": 0,
  "last_check_at": null,
  "last_receive_at": "2023-12-11T20:13:57Z",
  "created_at": "2021-05-07T11:42:58Z",
  "updated_at": "2021-05-07T11:47:00Z",
  "last_event_at": "2023-12-11T20:13:52Z",
  "last_error_log_at": null,
  "disabled": false,
  "guid": "f3fe6f8e167c9db42e64eaef8e5d2f0c",
  "group_id": null,
  "position": {
    "x": 105,
    "y": 195
  },
  "receivers": [70118],
  "sources": [70116],
  "team_id": 335,
  "monitor_failures": false,
  "monitor_all_events": false,
  "monitor_no_events_emitted": null,
  "time_saved_unit": "minutes",
  "time_saved_value": 0,
  "page": null,
  "story_id": 807,
  "story_mode": "LIVE",
  "nested_group_id": null,
  "links_to_sources": {
    "source_id": 70116,
    "link_type": "DEFAULT"
  },
  "links_to_receivers": {
    "receiver_id": 70118,
    "link_type": "NO_MATCH"
  }
}
```

#### Clear memory

## Description

Clear all the values stored in an action's memory of a specific action located on the 'Status' tab of the action. This is applicable for event transform actions in 'deduplicate' or 'implode' mode.
Action Memory allows users to store information generated during the execution of event transform actions and then reference or manipulate that information in subsequent actions within a workflow. Clearing this data can be of benefit to a large story with many events.

## Request

HTTP Method: **DELETE**

| Parameter | Description      |
| --------- | ---------------- |
| action_id | ID of the action |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/actions/<<action_id>>/clear_memory \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the action with cleared memory.

### Field description

| Parameter                 | Description                                                                                                 |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| id                        | Action ID.                                                                                                  |
| type                      | Action type                                                                                                 |
| user_id                   | User ID of the action's owner.                                                                              |
| options                   | JSON Options block of the action.                                                                           |
| name                      | Name of the action.                                                                                         |
| description               | A user-defined description of the action.                                                                   |
| schedule                  | An object defining the cron schedule for the action.                                                        |
| blended_events_count      | Number of events action has emitted in both TEST and LIVE mode.                                             |
| logs_count                | Number of logs action has stored.                                                                           |
| last_check_at             | ISO 8601 Timestamp representing date and time of action's last scheduled run.                               |
| last_receive_at           | ISO 8601 Timestamp representing date and time of last event received.                                       |
| created_at                | ISO 8601 Timestamp representing creation date and time of action.                                           |
| updated_at                | ISO 8601 Timestamp representing last updated date and time of action.                                       |
| last_event_at             | ISO 8601 Timestamp representing date and time the last event was received. Updated at one-minute intervals. |
| last_error_log_at         | ISO 8601 Timestamp representing date and time of last error thrown by the action.                           |
| disabled                  | Boolean flag indicating whether action is disabled.                                                         |
| group_id                  | ID of group to which the action belongs.                                                                    |
| guid                      | Unique identifier of the action.                                                                            |
| position                  | An object describing the XY coordinates of the action on the story diagram.                                 |
| story_id                  | ID of story to which the action belongs.                                                                    |
| story_mode                | Mode of the story to which the action belongs `LIVE` or `TEST`                                              |
| nested_group_id           | ID of group contained in the action.                                                                        |
| team_id                   | ID of team to which the action belongs.                                                                     |
| sources                   | An Array of Action IDs this action receives emitted events from.                                            |
| receivers                 | An Array of Action IDs this action emits events to.                                                         |
| monitor_failures          | Boolean flag indicating if a notification should be sent when this action fails.                            |
| monitor_all_events        | Boolean flag indicating if all events should be monitored.                                                  |
| monitor_no_events_emitted | Duration in seconds. If no events are emitted in this time, a notification will be sent.                    |
| time_saved_unit           | Unit of time corresponding to time saved value.                                                             |
| time_saved_value          | Number indicating the amount of time saved.                                                                 |
| page                      | An object with information on the associated page, if this is a page action                                 |
| action_memory_contents    | An object containing an array of the values an action may be holding in its memory.                         |
| slug                      | An underscored representation of the action's name                                                          |
| links_to_sources          | An array of links to source actions, including the source ID and the link type                              |
| links_to_receivers        | An array of links to receiver actions, including the receiver ID and the link type                          |

### Sample response

```json
{
  "id": 73563,
  "type": "Agents::EventTransformationAgent",
  "user_id": 167,
  "options": {
    "mode": "deduplicate",
    "path": "<<deduplicate_path.alert>>",
    "lookback": "10"
  },
  "name": "Deduplicate alerts",
  "description": null,
  "schedule": null,
  "blended_events_count": 1,
  "logs_count": 2,
  "last_check_at": null,
  "last_receive_at": "2023-12-11T20:13:57Z",
  "created_at": "2021-05-07T11:42:58Z",
  "updated_at": "2021-05-07T11:47:00Z",
  "last_event_at": "2023-12-11T20:13:52Z",
  "last_error_log_at": null,
  "disabled": false,
  "guid": "f3fe6f8e167c9db42e64eaef8e5d2f0c",
  "position": {
    "x": 105,
    "y": 195
  },
  "group_id": null,
  "receivers": [70118],
  "sources": [70116],
  "team_id": 335,
  "monitor_failures": false,
  "monitor_all_events": false,
  "monitor_no_events_emitted": null,
  "time_saved_unit": "minutes",
  "time_saved_value": 0,
  "page": null,
  "story_id": 807,
  "story_mode": "LIVE",
  "nested_group_id": null,
  "links_to_sources": {
    "source_id": 70116,
    "link_type": "DEFAULT"
  },
  "links_to_receivers": {
    "receiver_id": 70118,
    "link_type": "NO_MATCH"
  }
}
```

### Change request

#### Create

## Description

Create a change request.

## Request

HTTP Method: **POST**

| Query Parameter | Description                                                                                                                                            |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| title           | **Optional** The change request name.                                                                                                                  |
| description     | **Optional** A user-defined description of the change request. For mentioning users, use the notation <@user-2435>, replacing '2435' with the user ID. |
| draft_id        | **Optional** The ID of the draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints).                                    |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/change_request  \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "title": "Sample Change Request",
        "description": "A sample change request"
      }'
```

## Response

A successful request will return a JSON object describing the change request

### Field description

| Parameter      | Description                                                                                                                                                                                                                                                                                                                                                                                                                 |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id             | The live story ID.                                                                                                                                                                                                                                                                                                                                                                                                          |
| draft_id       | ID of the selected draft.                                                                                                                                                                                                                                                                                                                                                                                                   |
| draft_name     | Name of the selected draft.                                                                                                                                                                                                                                                                                                                                                                                                 |
| change_request | The change request. This contains `id`, the ID of the request, `name`, the name of the request, `description`, the description of the request, `status`, the status of the request which is one of "PENDING" or "APPROVED", `participants`, an array of user IDs that made changes in the change request, and `live_story_export` and `draft_export` which capture the export of the live story and the draft respectively. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 154,
  "draft_id": 1234,
  "draft_name": "Initial Draft",
  "change_request": {
    "id": 21,
    "name": "Change request name",
    "description": "Description of proposed changes",
    "status": "PENDING",
    "participants": [1, 2, 3],
    "live_story_export": "{\"schema_version\":26,\"standard_lib_version\":83,\"action_runtime_version\":35,\"name\":\"Story 1\",\"description\":\"Example description\",\"guid\":\"b20c18537050fcfbbf01b87ba3a82fef\",\"slug\":\"story_1\",\"agents\":[],\"diagram_notes\":[],\"links\":[],\"diagram_layout\":\"{}\",\"story_library_metadata\":{},\"monitor_failures\":false,\"synchronous_webhooks_enabled\":false,\"integrations\":[],\"parent_only_send_to_story\":false,\"keep_events_for\":604800,\"reporting_status\":true,\"send_to_story_enabled\":false,\"entry_agent_guid\":null,\"exit_agent_guids\":[],\"api_entry_action_guids\":[],\"api_exit_action_guids\":[],\"send_to_story_access\":null,\"send_to_story_access_source\":0,\"send_to_story_skill_use_requires_confirmation\":true,\"pages\":[],\"tags\":[],\"time_saved_unit\":\"minutes\",\"time_saved_value\":0,\"origin_story_identifier\":\"cloud:93b6b6f3c7c8d824afc9cb6d0b32454c:b20c18537050fcfbbf01b87ba3a82fef\",\"recipients\":[\"person_1@example.com\"],\"integration_product\":null,\"integration_vendor\":null,\"llm_product_instructions\":\"\",\"exported_at\":\"2023-10-16T11:16:25Z\"}",
    "draft_export": "{\"schema_version\":26,\"standard_lib_version\":83,\"action_runtime_version\":35,\"name\":\"Story 1\",\"description\":\"Example description\",\"guid\":\"a8e4709bbede0ce3db932c863285a747\",\"slug\":\"story_1_test0\",\"agents\":[{\"type\":\"Agents::EventTransformationAgent\",\"name\":\"New action\",\"disabled\":false,\"description\":null,\"guid\":\"c7b71ef79ca29ae68b7763714c12dd05\",\"origin_story_identifier\":\"cloud:93b6b6f3c7c8d824afc9cb6d0b32454c:a8e4709bbede0ce3db932c863285a747\",\"options\":{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"message\":\"This is an optional message\"}},\"reporting\":{\"time_saved_value\":0,\"time_saved_unit\":\"minutes\"},\"monitoring\":{\"monitor_all_events\":false,\"monitor_failures\":false,\"monitor_no_events_emitted\":null},\"template\":{\"created_from_template_guid\":null,\"created_from_template_version\":null,\"template_tags\":[]},\"width\":null,\"schedule\":null}],\"diagram_notes\":[],\"links\":[],\"diagram_layout\":\"{\\\"c7b71ef79ca29ae68b7763714c12dd05\\\":[300,300]}\",\"story_library_metadata\":{},\"monitor_failures\":false,\"synchronous_webhooks_enabled\":false,\"integrations\":[],\"parent_only_send_to_story\":false,\"keep_events_for\":604800,\"reporting_status\":true,\"send_to_story_enabled\":false,\"entry_agent_guid\":null,\"exit_agent_guids\":[],\"api_entry_action_guids\":[],\"api_exit_action_guids\":[],\"send_to_story_access\":null,\"send_to_story_access_source\":0,\"send_to_story_skill_use_requires_confirmation\":true,\"pages\":[],\"tags\":[],\"time_saved_unit\":\"minutes\",\"time_saved_value\":0,\"origin_story_identifier\":\"cloud:93b6b6f3c7c8d824afc9cb6d0b32454c:b20c18537050fcfbbf01b87ba3a82fef\",\"recipients\":[\"person_1@example.com\"],\"integration_product\":null,\"integration_vendor\":null,\"llm_product_instructions\":\"\",\"exported_at\":\"2023-10-16T11:16:25Z\"}"
  }
}
```

<!-- cspell:enable -->

#### Generate a change request description

## Description

Generate a new description for the changes captured in the change request. AI features must be enabled for a successful response.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                            |
| --------------- | ------------------------------------------------------------------------------------------------------ |
| draft_id        | The ID of the draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/change_request/ai_description  \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "draft_id": "<<draft_id>>"
      }'
```

## Response

A successful request will return a JSON object with the description of the changes captured in the change request.

### Field description

| Parameter                     | Description                                                              |
| ----------------------------- | ------------------------------------------------------------------------ |
| change_request_ai_description | The generated description of the changes captured in the change request. |

### Sample response

<!-- cspell:disable -->

```json
{
  "change_request_ai_description": "Summary of changes"
}
```

<!-- cspell:enable -->

#### Approve

## Description

Approve a change request.

## Request

HTTP Method: **POST**

| Query Parameter   | Description                  |
| ----------------- | ---------------------------- |
| change_request_id | The ID of the change request |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/change_request/approve  \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "change_request_id": "<<change_request_id>>"
      }'
```

## Response

A successful request will return a JSON object with the status of the change request

### Field description

| Parameter      | Description                                                                                                                                                                                                                  |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| draft_id       | ID of the draft containing the change request.                                                                                                                                                                               |
| draft_name     | Name of the draft containing the change request                                                                                                                                                                              |
| id             | The live story ID.                                                                                                                                                                                                           |
| change_request | The change request. This contains `id`, the ID of the request, `status`, the status of the request which is one of "PENDING" or "APPROVED", and `participants`, an array of user IDs that made changes in the change request |

### Sample response

<!-- cspell:disable -->

```json
{
  "draft_id": 1234,
  "draft_name": "Initial draft",
  "change_request": {
    "id": 15,
    "status": "APPROVED",
    "participants": [1, 2, 3]
  },
  "id": 154
}
```

<!-- cspell:enable -->

#### Cancel

## Description

Cancel a change request.

## Request

HTTP Method: **POST**

| Query Parameter   | Description                  |
| ----------------- | ---------------------------- |
| change_request_id | The ID of the change request |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/change_request/cancel  \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "change_request_id": "<<change_request_id>>"
      }'
```

## Response

A successful request will return a JSON object that shows the change request has been deleted

### Field description

| Parameter      | Description                                                  |
| -------------- | ------------------------------------------------------------ |
| draft_id       | ID of the draft containing the change request.               |
| draft_name     | Name of the draft containing the change request.             |
| id             | The live story ID.                                           |
| change_request | The change request, which will be `null` as it was canceled. |

### Sample response

<!-- cspell:disable -->

```json
{
  "draft_id": 1234,
  "draft_name": "Initial draft",
  "change_request": null,
  "id": 154
}
```

<!-- cspell:enable -->

#### Promote

## Description

Promote changes to the live story.

## Request

HTTP Method: **POST**

| Query Parameter   | Description                  |
| ----------------- | ---------------------------- |
| change_request_id | The ID of the change request |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/change_request/promote  \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "change_request_id": "<<change_request_id>>"
      }'
```

## Response

A successful request will return a JSON object representing updated story.

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                    |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name.                                                                                                               |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | The mode of the story. `LIVE` or `TEST`                                                                                                                        |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on all actions in the story. This will be false when only some actions have monitoring enabled.         |
| actions_with_monitoring                       | List of action IDs with monitoring enabled.                                                                                                                    |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                   |
| api_enabled                                   | Boolean flag indicating if Webhook API is enabled.                                                                                                             |
| api_entry_actions                             | Array of entry action ID for API enabled story.                                                                                                                |
| api_exit_actions                              | Array of exit action IDs for API enabled story.                                                                                                                |
| api_name                                      | Name of API for API enabled story.                                                                                                                             |
| id                                            | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |

### Sample response

```json
{
  "name": "Simple story",
  "user_id": 167,
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "send_to_story_skill_use_requires_confirmation": true,
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "slug": "simple_story",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "LIVE",
  "monitor_failures": false,
  "actions_with_monitoring": [],
  "recipients": [
    {
      "address": "test@example.com"
    }
  ],
  "api_enabled": false,
  "api_entry_actions": [],
  "api_exit_actions": [],
  "api_name": null,
  "id": 7981,
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1]
}
```

#### View

## Description

View the current change request for a story.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                                         |
| --------------- | ------------------------------------------------------------------------------------------------------------------- |
| draft_id        | **Optional** The ID of the draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/change_request/view  \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return a JSON object describing the change request, if a change request exists. If a test story has changes but a change request has not been submitted for review, change_request will be null.

### Field description

| Parameter      | Description                                                                                                                                                                                                                                                                                                                                                                                                                 |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id             | The live story ID.                                                                                                                                                                                                                                                                                                                                                                                                          |
| draft_id       | ID of the selected draft.                                                                                                                                                                                                                                                                                                                                                                                                   |
| draft_name     | Name of the selected draft.                                                                                                                                                                                                                                                                                                                                                                                                 |
| change_request | The change request. This contains `id`, the ID of the request, `name`, the name of the request, `description`, the description of the request, `status`, the status of the request which is one of "PENDING" or "APPROVED", `participants`, an array of user IDs that made changes in the change request, and `live_story_export` and `draft_export` which capture the export of the live story and the draft respectively. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 154,
  "draft_id": 1234,
  "draft_name": "Initial Draft",
  "change_request": {
    "id": 21,
    "name": "Change request name",
    "description": "Description of proposed changes",
    "status": "PENDING",
    "participants": [1, 2, 3],
    "live_story_export": "{\"schema_version\":26,\"standard_lib_version\":83,\"action_runtime_version\":35,\"name\":\"Story 1\",\"description\":\"Example description\",\"guid\":\"b20c18537050fcfbbf01b87ba3a82fef\",\"slug\":\"story_1\",\"agents\":[],\"diagram_notes\":[],\"links\":[],\"diagram_layout\":\"{}\",\"story_library_metadata\":{},\"monitor_failures\":false,\"synchronous_webhooks_enabled\":false,\"integrations\":[],\"parent_only_send_to_story\":false,\"keep_events_for\":604800,\"reporting_status\":true,\"send_to_story_enabled\":false,\"entry_agent_guid\":null,\"exit_agent_guids\":[],\"api_entry_action_guids\":[],\"api_exit_action_guids\":[],\"send_to_story_access\":null,\"send_to_story_access_source\":0,\"send_to_story_skill_use_requires_confirmation\":true,\"pages\":[],\"tags\":[],\"time_saved_unit\":\"minutes\",\"time_saved_value\":0,\"origin_story_identifier\":\"cloud:93b6b6f3c7c8d824afc9cb6d0b32454c:b20c18537050fcfbbf01b87ba3a82fef\",\"recipients\":[\"person_1@example.com\"],\"integration_product\":null,\"integration_vendor\":null,\"llm_product_instructions\":\"\",\"exported_at\":\"2023-10-16T11:16:25Z\"}",
    "draft_export": "{\"schema_version\":26,\"standard_lib_version\":83,\"action_runtime_version\":35,\"name\":\"Story 1\",\"description\":\"Example description\",\"guid\":\"a8e4709bbede0ce3db932c863285a747\",\"slug\":\"story_1_test0\",\"agents\":[{\"type\":\"Agents::EventTransformationAgent\",\"name\":\"New action\",\"disabled\":false,\"description\":null,\"guid\":\"c7b71ef79ca29ae68b7763714c12dd05\",\"origin_story_identifier\":\"cloud:93b6b6f3c7c8d824afc9cb6d0b32454c:a8e4709bbede0ce3db932c863285a747\",\"options\":{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"message\":\"This is an optional message\"}},\"reporting\":{\"time_saved_value\":0,\"time_saved_unit\":\"minutes\"},\"monitoring\":{\"monitor_all_events\":false,\"monitor_failures\":false,\"monitor_no_events_emitted\":null},\"template\":{\"created_from_template_guid\":null,\"created_from_template_version\":null,\"template_tags\":[]},\"width\":null,\"schedule\":null}],\"diagram_notes\":[],\"links\":[],\"diagram_layout\":\"{\\\"c7b71ef79ca29ae68b7763714c12dd05\\\":[300,300]}\",\"story_library_metadata\":{},\"monitor_failures\":false,\"synchronous_webhooks_enabled\":false,\"integrations\":[],\"parent_only_send_to_story\":false,\"keep_events_for\":604800,\"reporting_status\":true,\"send_to_story_enabled\":false,\"entry_agent_guid\":null,\"exit_agent_guids\":[],\"api_entry_action_guids\":[],\"api_exit_action_guids\":[],\"send_to_story_access\":null,\"send_to_story_access_source\":0,\"send_to_story_skill_use_requires_confirmation\":true,\"pages\":[],\"tags\":[],\"time_saved_unit\":\"minutes\",\"time_saved_value\":0,\"origin_story_identifier\":\"cloud:93b6b6f3c7c8d824afc9cb6d0b32454c:b20c18537050fcfbbf01b87ba3a82fef\",\"recipients\":[\"person_1@example.com\"],\"integration_product\":null,\"integration_vendor\":null,\"llm_product_instructions\":\"\",\"exported_at\":\"2023-10-16T11:16:25Z\"}"
  }
}
```

<!-- cspell:enable -->

### Disable

## Description

Update the disabled state of a story. This method can be used to disable a story that has change control enabled, bypassing the change control approval process.

## Request

HTTP Method: **POST**

| Parameter | Description                                                  |
| --------- | ------------------------------------------------------------ |
| id        | The ID of the story to update.                               |
| disabled  | Boolean flag indicating the new disabled state of the story. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<id>>/disable \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{"disabled": true}'
```

## Response

A successful request will return a JSON object representing the specified disabled state.

### Field description

| Parameter | Description                                              |
| --------- | -------------------------------------------------------- |
| disabled  | Boolean flag indicating the disabled state of the story. |

### Sample response

```json
{
  "disabled": true
}
```

### Drafts

#### Create

## Description

Create a new draft for a story.

## Request

HTTP Method: **POST**

| Path Parameter | Description     |
| -------------- | --------------- |
| story_id       | ID of the story |

| Query Parameter | Description       |
| --------------- | ----------------- |
| name            | Name of the draft |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/drafts \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "My draft",
      }'
```

## Response

A successful request will return a JSON object describing the created draft.

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                            | The draft ID                                                                                                                                                   |
| name                                          | The draft name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                    |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name. Will update if the draft updates the story name. Includes a draft-specific suffix.                            |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | Legacy field, always `TEST`.                                                                                                                                   |
| story_id                                      | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |

### Sample response

<!-- cspell:disable -->

```json
{
  "name": "My draft",
  "id": 100,
  "user_id": 167,
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "send_to_story_skill_use_requires_confirmation": true,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "TEST",
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1],
  "story_name": "My story",
  "story_id": 99,
  "slug": "my_story_test0"
}
```

#### List

## Description

Retrieve a list of drafts for a story.

## Request

HTTP Method: **GET**

| Path Parameter | Description     |
| -------------- | --------------- |
| story_id       | ID of the story |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/drafts \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON array describing drafts for that Tines story.

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                            | The draft ID                                                                                                                                                   |
| name                                          | The draft name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                    |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name. Will update if the draft updates the story name. Includes a draft-specific suffix.                            |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | Legacy field, always `TEST`.                                                                                                                                   |
| story_id                                      | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |

### Sample response

<!-- cspell:disable -->

```json
{
  "drafts": [
    {
      "name": "My draft",
      "id": 100,
      "user_id": 167,
      "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
      "keep_events_for": 604800,
      "disabled": false,
      "priority": false,
      "send_to_story_enabled": false,
      "send_to_story_access_source": "OFF",
      "send_to_story_access": null,
      "send_to_story_skill_use_requires_confirmation": true,
      "shared_team_slugs": [],
      "entry_agent_id": null,
      "exit_agents": [],
      "team_id": 1,
      "tags": ["Tag 1", "Tag 2"],
      "guid": "df1e838a18d20696120b41516497b017",
      "created_at": "2021-05-10T08:56:50Z",
      "updated_at": "2021-05-10T08:56:50Z",
      "edited_at": "2021-05-10T08:56:50Z",
      "mode": "TEST",
      "folder_id": 1,
      "published": true,
      "change_control_enabled": false,
      "locked": false,
      "owners": [1],
      "story_name": "My story",
      "story_id": 99,
      "slug": "my_story_test0"
    },
    {
      "name": "My other draft",
      "id": 101,
      "user_id": 167,
      "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
      "keep_events_for": 604800,
      "disabled": false,
      "priority": false,
      "send_to_story_enabled": false,
      "send_to_story_access_source": "OFF",
      "send_to_story_access": null,
      "send_to_story_skill_use_requires_confirmation": true,
      "shared_team_slugs": [],
      "entry_agent_id": null,
      "exit_agents": [],
      "team_id": 1,
      "tags": ["Tag 1", "Tag 2"],
      "guid": "df1e838a18d20696120b41516497b017",
      "created_at": "2021-05-10T08:56:50Z",
      "updated_at": "2021-05-10T08:56:50Z",
      "edited_at": "2021-05-10T08:56:50Z",
      "mode": "TEST",
      "folder_id": 1,
      "published": true,
      "change_control_enabled": false,
      "locked": false,
      "owners": [1],
      "story_name": "My story",
      "story_id": 99,
      "slug": "my_story_test1"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/stories/99/drafts?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20, // Max: 500
    "pages": 1,
    "count": 2
  }
}
```

#### Delete

## Description

Delete a draft for a story.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description     |
| -------------- | --------------- |
| story_id       | ID of the story |
| draft_id       | ID of the draft |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/drafts/<<draft_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a 204 (no content) response.

### Events

#### Get

## Description

Retrieve an event.

## Request

HTTP Method: **GET**

| Parameter | Description                      |
| --------- | -------------------------------- |
| event_id  | The ID of the event to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/events/<<event_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified event.

### Field description

| Parameter           | Description                                                                                                        |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| id                  | Event ID.                                                                                                          |
| agent_id            | ID of action that emitted the event.                                                                               |
| payload             | JSON representation of the event payload.                                                                          |
| created_at          | ISO 8601 Timestamp representing date and time the event was emitted.                                               |
| updated_at          | ISO 8601 Timestamp representing date and time the event was last updated. Will always be the same as 'created_at'. |
| story_run_guid      | Unique identifier of that event's story run.                                                                       |
| previous_events_ids | IDs of the upstream action's events in that event's story run.                                                     |
| re_emitted          | Boolean flag indicating whether this event has been re-emitted.                                                    |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 1785421735,
  "user_id": 1,
  "agent_id": 21,
  "payload": {
    "propagation_webhook": {
      "username": "joe.bloggs",
      "age": 99
    },
    "delay_http_request": {
      "body": {
        "delay": "5"
      },
      "headers": {
        "access-control-allow-credentials": "",
        "access-control-allow-headers": "",
        "access-control-allow-methods": "",
        "access-control-allow-origin": "",
        "access-control-expose-headers": "",
        "content-type": "application/json; charset=utf-8",
        "date": "Tue, 03 Apr 2018 13:37:24 GMT",
        "etag": "W/\"d-N6WkUL2Jgn7fCGyPWbbptw\"",
        "server": "nginx",
        "set-cookie": "sails.sid=s%3A6Hr22AAbEOurnGiqRo3; Path=/; HttpOnly",
        "vary": "Accept-Encoding",
        "content-length": "13",
        "connection": "Close"
      },
      "status": 200
    }
  },
  "created_at": "2018-04-03T13:37:24.479Z",
  "updated_at": "2018-04-03T13:37:24.479Z",
  "story_run_guid": "d59cd024-38b2-4287-b298-a8ee8dc86863",
  "previous_events_ids": [1785155927],
  "re_emitted": false
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of events.

## Request

HTTP Method: **GET**

| Parameter                  | Description                                                                                                                          |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| since_id                   | **Optional** Only retrieve events after this ID.                                                                                     |
| until_id                   | **Optional** Only retrieve events until (and including) this ID.                                                                     |
| since                      | **Optional** Only retrieve events created after this time (ISO 8601 timestamp).                                                      |
| until                      | **Optional** Only retrieve events until this time (ISO 8601 timestamp).                                                              |
| team_id                    | **Optional** Filter by the given team.                                                                                               |
| story_id                   | **Optional** Filter by the given story.                                                                                              |
| include_groups             | **Optional** Include events from groups in the specified story. Only valid if `story_id` is specified. Boolean, defaults to `false`. |
| show_reemitted_events_only | **Optional** Only retrieve events that have been re-emitted. Boolean, defaults to `false`.                                           |
| per_page                   | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 500.                                            |
| page                       | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                  |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/events \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing events in the Tines tenant.

### Field description

| Parameter           | Description                                                                                                        |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| id                  | Event ID.                                                                                                          |
| agent_id            | ID of action that emitted the event.                                                                               |
| payload             | JSON representation of the event payload.                                                                          |
| created_at          | ISO 8601 Timestamp representing date and time the event was emitted.                                               |
| updated_at          | ISO 8601 Timestamp representing date and time the event was last updated. Will always be the same as 'created_at'. |
| story_run_guid      | Unique identifier of that event's story run.                                                                       |
| previous_events_ids | IDs of the upstream action's events in that event's story run.                                                     |
| re_emitted          | Boolean flag indicating whether this event has been re-emitted.                                                    |

### Sample response

<!-- cspell:disable -->

```json
{
  "events": [
    {
      "id": 1785421735,
      "user_id": 1,
      "agent_id": 21,
      "payload": {
        "propagation_webhook": {
          "username": "joe.bloggs",
          "age": 99
        },
        "delay_http_request": {
          "body": {
            "delay": "5"
          },
          "headers": {
            "access-control-allow-credentials": "",
            "access-control-allow-headers": "",
            "access-control-allow-methods": "",
            "access-control-allow-origin": "",
            "access-control-expose-headers": "",
            "content-type": "application/json; charset=utf-8",
            "date": "Tue, 03 Apr 2018 13:37:24 GMT",
            "etag": "W/\"d-N6WkUL2Jgn7fCGyPWbbptw\"",
            "server": "nginx",
            "set-cookie": "sails.sid=s%3A6Hr22AAbEOurnGiqRo3; Path=/; HttpOnly",
            "vary": "Accept-Encoding",
            "content-length": "13",
            "connection": "Close"
          },
          "status": 200
        }
      },
      "created_at": "2018-04-03T13:37:24.479Z",
      "updated_at": "2018-04-03T13:37:24.479Z",
      "story_run_guid": "d59cd024-38b2-4287-b298-a8ee8dc86863",
      "previous_events_ids": [1785155927],
      "re_emitted": false
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/events?page=1&per_page=20",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

### Large payloads

Please note that a response payload size exceeding 200MB will result in a 422 error.

<!-- cspell:enable -->

#### Re-emit

## Description

Re&#x2011;emit an event.

## Request

HTTP Method: **POST**

| Parameter | Description                     |
| --------- | ------------------------------- |
| event_id  | The ID of the event to re-emit. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/events/<<event_id>>/reemit \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Groups

#### List run events

## Description

Retrieve a list of events for a group run.

## Request

HTTP Method: **GET**

| Path Parameter | Description                |
| -------------- | -------------------------- |
| group_id       | The ID of the group.       |
| group_run_guid | The guid of the group run. |

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| per_page        | **Optional** Set the number of results returned per page.                                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
 https://<tenant-domain>/api/v1/groups/<<group_id>>/runs/<<group_run_guid>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON Array describing the events for a group run.

### Field description

| Parameter    | Description                                                          |
| ------------ | -------------------------------------------------------------------- |
| id           | ID of the event.                                                     |
| action_id    | ID of action that emitted the event.                                 |
| created_at   | ISO 8601 Timestamp representing date and time the event was emitted. |
| is_reemitted | Boolean flag indicating whether this event has been re-emitted.      |

<!-- cspell:disable -->

### Sample response

```json
{
  "group_run_events": [
    {
      "id": 1126,
      "action_id": 3775,
      "created_at": "2023-12-13T16:19:50Z",
      "is_reemitted": false
    },
    {
      "id": 1131,
      "action_id": 3809,
      "created_at": "2023-12-13T16:19:50Z",
      "is_reemitted": false
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/groups/<<group_id>>/runs/<<group_run_guid>>?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

#### List runs

## Description

Retrieve a list of group runs.

## Request

HTTP Method: **GET**

| Path Parameter | Description          |
| -------------- | -------------------- |
| group_id       | The ID of the group. |

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| per_page        | **Optional** Set the number of results returned per page.                                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
 https://<tenant-domain>/api/v1/groups/<<group_id>>/runs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON Array describing the group runs for a group.

### Field description

| Parameter    | Description                                    |
| ------------ | ---------------------------------------------- |
| guid         | Guid of the group run.                         |
| duration     | Duration of the group run.                     |
| story_id     | ID of the parent story.                        |
| group_id     | ID of the group.                               |
| start_time   | The time the group run started.                |
| end_time     | The time the group run ended.                  |
| action_count | Number of actions in the run.                  |
| event_count  | Number of events in the run.                   |
| story_mode   | The mode of the parent story `LIVE` or `TEST`. |
| draft_id     | ID of the draft to which the group belongs.    |
| draft_name   | Name of the draft to which the group belongs.  |

<!-- cspell:disable -->

### Sample response

```json
{
  "group_runs": [
    {
      "guid": "5466f325-0bc6-47fb-943f-41d1a2a8d6ae",
      "duration": 0,
      "story_id": 155,
      "group_id": 200,
      "start_time": "2023-12-13T16:19:50Z",
      "end_time": "2023-12-13T16:19:50Z",
      "action_count": 5,
      "event_count": 5,
      "story_mode": "LIVE",
      "draft_id": 12345,
      "draft_name": "My draft"
    },
    {
      "guid": "c0ea6684-1cfb-4ac5-858b-79c631daa79f",
      "duration": 0,
      "story_id": 155,
      "group_id": 200,
      "start_time": "2023-12-13T16:16:49Z",
      "end_time": "2023-12-13T16:16:49Z",
      "action_count": 1,
      "event_count": 1,
      "story_mode": "LIVE",
      "draft_id": 12345,
      "draft_name": "My draft"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/groups/<<group_id>>/runs?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Links

#### Delete

## Description

Delete one or more links between actions. All links must belong to the same story.

## Request

HTTP Method: **DELETE**

| Parameter   | Description                                                                                 |
| ----------- | ------------------------------------------------------------------------------------------- |
| links       | An array of link objects to delete. Each object must include `source_id` and `receiver_id`. |
| source_id   | The ID of the source action.                                                                |
| receiver_id | The ID of the receiver action.                                                              |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/links \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "links": [
          {
            "source_id": "123",
            "receiver_id": "456"
          }
        ]
      }'
```

### Bulk delete

Multiple links can be deleted in a single request. All links must belong to the same story.

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/links \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "links": [
          {
            "source_id": "123",
            "receiver_id": "456"
          },
          {
            "source_id": "456",
            "receiver_id": "789"
          }
        ]
      }'
```

## Response

A successful request will return an empty response with a `204` status code.

### Notes

#### Create

## Description

Create a note on the storyboard. Story or Group ID must be provided. Defaults to a draft called `test` if change control is enabled on the story and no draft_id is provided.

## Request

HTTP Method: **POST**

| Parameter | Description                                                                                                                                       |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| story_id  | **Optional** ID of story to which the note should be added.                                                                                       |
| group_id  | **Optional** ID of group to which the note should be added.                                                                                       |
| content   | The note [Markdown](https://www.markdownguide.org/basic-syntax/) formatted content.                                                               |
| position  | **Optional** An object describing the XY coordinates of the position on the story diagram.                                                        |
| draft_id  | **Optional** ID of the draft to which the note should be added. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/notes/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "story_id": 813,
        "content": "# Header\n This is a list \n - item 1"
      }'
```

## Response

A successful request will return a JSON object describing the created note.

### Field description

| Parameter  | Description                                                                         |
| ---------- | ----------------------------------------------------------------------------------- |
| id         | The note ID.                                                                        |
| story_id   | ID of story to which the note belongs.                                              |
| story_mode | Mode of the story to which the note belongs `LIVE` or `TEST`                        |
| group_id   | ID of group to which the note belongs.                                              |
| content    | The note [Markdown](https://www.markdownguide.org/basic-syntax/) formatted content. |
| position   | An object describing the XY coordinates of the position on the story diagram.       |
| draft_id   | ID of the draft to which the note belongs.                                          |
| draft_name | Name of the draft to which the note belongs.                                        |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 21405,
  "story_id": 813,
  "story_mode": "LIVE",
  "group_id": null,
  "position": {
    "x": 0,
    "y": 0
  },
  "content": "# Header\n This is a list \n - item 1",
  "draft_id": 12345,
  "draft_name": "My draft"
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a note.

## Request

HTTP Method: **GET**

| Parameter | Description                     |
| --------- | ------------------------------- |
| note_id   | The ID of the note to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/notes/<<note_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified note.

### Field description

| Parameter  | Description                                                                         |
| ---------- | ----------------------------------------------------------------------------------- |
| id         | The note ID.                                                                        |
| story_id   | ID of story to which the note belongs.                                              |
| story_mode | Mode of the story to which the note belongs `LIVE` or `TEST`                        |
| group_id   | ID of group to which the note belongs.                                              |
| content    | The note [Markdown](https://www.markdownguide.org/basic-syntax/) formatted content. |
| position   | An object describing the XY coordinates of the position on the story diagram.       |

### Sample response

```json
{
  "id": 21405,
  "story_id": 813,
  "story_mode": "LIVE",
  "group_id": null,
  "position": {
    "x": 0,
    "y": 0
  },
  "content": "# Header\n This is a list \n - item 1"
}
```

#### Update

## Description

Update a note.

## Request

HTTP Method: **PUT**

| Parameter | Description                                                                                      |
| --------- | ------------------------------------------------------------------------------------------------ |
| content   | **Optional** The note [Markdown](https://www.markdownguide.org/basic-syntax/) formatted content. |
| position  | **Optional** An object describing the XY coordinates of the position on the story diagram.       |

| Path Parameter | Description    |
| -------------- | -------------- |
| note_id        | ID of the note |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/notes/<<note_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "content": "# Header\n This is a list \n - item 1\n - item 2",
        "position": {
          "x": 10,
          "y": 10
        }
      }'
```

## Response

A successful request will return a JSON object describing the updated note.

### Field description

| Parameter  | Description                                                                         |
| ---------- | ----------------------------------------------------------------------------------- |
| id         | The note ID.                                                                        |
| story_id   | ID of story to which the note belongs.                                              |
| story_mode | Mode of the story to which the note belongs `LIVE` or `TEST`                        |
| group_id   | ID of group to which the note belongs.                                              |
| content    | The note [Markdown](https://www.markdownguide.org/basic-syntax/) formatted content. |
| position   | An object describing the XY coordinates of the position on the story diagram.       |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 21405,
  "story_id": 813,
  "story_mode": "LIVE",
  "group_id": null,
  "position": {
    "x": 15,
    "y": 15
  },
  "content": "# Header\n This is a list \n - item 1\n - item 2"
}
```

<!-- cspell:enable -->

#### List

## Description

List notes.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                                                    |
| --------- | ------------------------------------------------------------------------------------------------------------------------------ |
| story_id  | **Optional** List notes for the given story                                                                                    |
| mode      | **Optional** List notes for the given story mode (`LIVE` or `TEST`, must be used with `story_id`)                              |
| team_id   | **Optional** List notes for the given team                                                                                     |
| group_id  | **Optional** List notes for the given group                                                                                    |
| per_page  | **Optional** Set the number of results returned per page.                                                                      |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                            |
| draft_id  | **Optional** List notes for the given draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/notes/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an array listing the requested notes.

### Field description

| Parameter  | Description                                                                         |
| ---------- | ----------------------------------------------------------------------------------- |
| id         | The note ID.                                                                        |
| story_id   | ID of story to which the note belongs.                                              |
| story_mode | Mode of the story to which the note belongs `LIVE` or `TEST`                        |
| group_id   | ID of group to which the note belongs.                                              |
| content    | The note [Markdown](https://www.markdownguide.org/basic-syntax/) formatted content. |
| position   | An object describing the XY coordinates of the position on the story diagram.       |
| draft_id   | ID of the draft to which the note belongs.                                          |
| draft_name | Name of the draft to which the note belongs.                                        |

### Sample response

```json
{
  "annotations": [
    {
      "id": 21405,
      "story_id": 813,
      "story_mode": "LIVE",
      "group_id": null,
      "position": {
        "x": 0,
        "y": 0
      },
      "content": "# Example note",
      "draft_id": 12345,
      "draft_name": "My draft"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/notes?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

## Description

Delete a note.

## Request

HTTP Method: **DELETE**

| Parameter | Description                   |
| --------- | ----------------------------- |
| note_id   | The ID of the note to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/notes/<<note_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Owners

#### Create

## Description

Create a story owner for a story.

## Request

HTTP Method: **POST**

| Query Parameter | Description                                                                                                                |
| --------------- | -------------------------------------------------------------------------------------------------------------------------- |
| story_id        | ID of the story in which the story owner should be created.                                                                |
| user_id         | The ID of the user that should be added as a story owner. The user must be a member of the team in which the story exists. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/owners \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"user_id":"<<user_id>>"}'
```

## Response

A successful request will return a JSON object describing the story in which the owner was created.

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                    |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name.                                                                                                               |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | The mode of the story. `LIVE` or `TEST`                                                                                                                        |
| id                                            | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on all actions in the story. This will be false when only some actions have monitoring enabled.         |
| actions_with_monitoring                       | List of action IDs with monitoring enabled.                                                                                                                    |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                   |

### Sample response

<!-- cspell:disable -->

```json
{
  "name": "Simple story",
  "user_id": 167,
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "send_to_story_skill_use_requires_confirmation": true,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "slug": "simple_story",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "LIVE",
  "id": 7981,
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1],
  "monitor_failures": false,
  "actions_with_monitoring": [],
  "recipients": [
    {
      "address": "test@example.com"
    }
  ]
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a story owner for a story.

## Request

HTTP Method: **DELETE**

| Parameter | Description                                                                                           |
| --------- | ----------------------------------------------------------------------------------------------------- |
| story_id  | The ID of the story in which the story owner should be deleted.                                       |
| user_id   | The ID of the user that should be removed as a story owner. The user must be an existing story owner. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/owners \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'  \
  -d '{
        "user_id": <<user_id>>
      }'
```

## Response

A successful request will return an empty response with a `204` status code.

### Recipients

#### Create

## Description

Create a story recipient for a story. The recipients will receive story monitoring communications when enabled.

## Request

HTTP Method: **POST**

| Query Parameter | Description                                                                                                                   |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| story_id        | ID of the story in which the story recipient should be created.                                                               |
| address         | The email or webhook URL that should receive story monitoring communications.                                                 |
| draft_id        | **Optional** The ID of the draft to update. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/recipients \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "address": "alice@example.com"
      }'
```

## Response

A successful request will return a JSON object describing the story in which the recipient was created.

### Field description

| Parameter                                     | Description                                                                                                                                                    |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                          | The story name.                                                                                                                                                |
| user_id                                       | ID of story creator.                                                                                                                                           |
| description                                   | A user-defined description of the story.                                                                                                                       |
| keep_events_for                               | Defined event retention period in seconds.                                                                                                                     |
| disabled                                      | Boolean flag indicating whether story is disabled.                                                                                                             |
| priority                                      | Boolean flag indicating whether story is high priority story.                                                                                                  |
| send_to_story_enabled                         | Boolean flag indicating if Send to Story is enabled.                                                                                                           |
| send_to_story_access_source                   | `STS`, `STS_AND_WORKBENCH`, `WORKBENCH` or `OFF` indicating where the send to story can be used.                                                               |
| send_to_story_access                          | Controls who is allowed to send to this story (`TEAM`,`GLOBAL`,`SPECIFIC_TEAMS`).                                                                              |
| send_to_story_referring_story_ids             | List of story ids that call this Send to Story enabled story. Only present when `send_to_story_enabled` is `true`.                                             |
| shared_team_slugs                             | List of teams' slugs that can send to this story when `send_to_story_access` is `SPECIFIC_TEAMS`, otherwise empty.                                             |
| send_to_story_skill_use_requires_confirmation | Boolean flag indicating whether workbench should ask for confirmation before running this story.                                                               |
| entry_agent_id                                | The ID of the entry action for this story.                                                                                                                     |
| exit_agents                                   | An Array of objects describing exit actions for this story.                                                                                                    |
| team_id                                       | ID of team to which the story belongs.                                                                                                                         |
| tags                                          | An array of tags used to classify the story.                                                                                                                   |
| guid                                          | Unique identifier of the story.                                                                                                                                |
| slug                                          | An underscored representation of the story name.                                                                                                               |
| created_at                                    | ISO 8601 Timestamp representing date and time the story was created.                                                                                           |
| updated_at                                    | ISO 8601 Timestamp representing date and time the story row was last updated. It is best to use edited_at to track any modifications made to the story itself. |
| edited_at                                     | ISO 8601 Timestamp representing date and time the story was last logically updated.                                                                            |
| mode                                          | The mode of the story. `LIVE` or `TEST`                                                                                                                        |
| id                                            | The story ID.                                                                                                                                                  |
| folder_id                                     | ID of folder to which the story belongs.                                                                                                                       |
| published                                     | Boolean flag indicating whether the story is published.                                                                                                        |
| change_control_enabled                        | Boolean flag indicating if change control is enabled.                                                                                                          |
| locked                                        | Boolean flag indicating whether the story is locked to changes.                                                                                                |
| owners                                        | List of user IDs that are listed as owners on the story.                                                                                                       |
| monitor_failures                              | Boolean flag indicating if monitor failures is enabled on all actions in the story. This will be false when only some actions have monitoring enabled.         |
| actions_with_monitoring                       | List of action IDs with monitoring enabled.                                                                                                                    |
| recipients                                    | List of monitoring recipients for the story.                                                                                                                   |

### Sample response

<!-- cspell:disable -->

```json
{
  "name": "Simple story",
  "user_id": 167,
  "description": "In the simple story we will create a fictional situation where a detection system is configured to send alerts to our Tines tenant",
  "keep_events_for": 604800,
  "disabled": false,
  "priority": false,
  "send_to_story_enabled": false,
  "send_to_story_access_source": "OFF",
  "send_to_story_access": null,
  "send_to_story_skill_use_requires_confirmation": true,
  "shared_team_slugs": [],
  "entry_agent_id": null,
  "exit_agents": [],
  "team_id": 1,
  "tags": ["Tag 1", "Tag 2"],
  "guid": "df1e838a18d20696120b41516497b017",
  "slug": "simple_story",
  "created_at": "2021-05-10T08:56:50Z",
  "updated_at": "2021-05-10T08:56:50Z",
  "edited_at": "2021-05-10T08:56:50Z",
  "mode": "LIVE",
  "id": 7981,
  "folder_id": 1,
  "published": true,
  "change_control_enabled": false,
  "locked": false,
  "owners": [1],
  "monitor_failures": false,
  "actions_with_monitoring": [],
  "recipients": [
    {
      "address": "test@example.com"
    }
  ]
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a story recipient for a story. The recipient will no longer receive story monitoring communications and will be removed from the recipients list.

## Request

HTTP Method: **DELETE**

| Parameter | Description                                                                                                                   |
| --------- | ----------------------------------------------------------------------------------------------------------------------------- |
| story_id  | The ID of the story in which the story recipient should be deleted.                                                           |
| address   | The email or webhook URL that should no longer receive story monitoring communications.                                       |
| draft_id  | **Optional** The ID of the draft to update. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/recipients \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "address": "alice@example.com"
      }'
```

## Response

A successful request will return an empty response with a `204` status code.

### Story runs

#### List run events

## Description

Retrieve a list of events for a story run.

## Request

HTTP Method: **GET**

| Path Parameter | Description                |
| -------------- | -------------------------- |
| story_id       | The ID of the story.       |
| story_run_guid | The guid of the story run. |

| Query Parameter | Description                                                                                                                        |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| story_mode      | **Optional** The mode of the story.                                                                                                |
| per_page        | **Optional** Set the number of results returned per page.                                                                          |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                |
| draft_id        | **Optional** Return events for a specific draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

```bash
curl -X GET \
 https://<tenant-domain>/api/v1/stories/<<story_id>>/runs/<<story_run_guid>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON Array describing the events for a story run.

### Field description

| Parameter    | Description                                                          |
| ------------ | -------------------------------------------------------------------- |
| id           | ID of the event.                                                     |
| action_id    | ID of action that emitted the event.                                 |
| created_at   | ISO 8601 Timestamp representing date and time the event was emitted. |
| is_reemitted | Boolean flag indicating whether this event has been re-emitted.      |

<!-- cspell:disable -->

### Sample response

```json
{
  "story_run_events": [
    {
      "id": 1126,
      "action_id": 3775,
      "created_at": "2023-12-13T16:19:50Z",
      "is_reemitted": false
    },
    {
      "id": 1131,
      "action_id": 3809,
      "created_at": "2023-12-13T16:19:50Z",
      "is_reemitted": false
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/stories/<<story_id>>/runs/<<story_run_guid>>?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

#### List runs

## Description

Retrieve a list of story runs.

## Request

HTTP Method: **GET**

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

| Query Parameter | Description                                                                                                                      |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| story_mode      | **Optional** The mode of the story.                                                                                              |
| per_page        | **Optional** Set the number of results returned per page.                                                                        |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                              |
| draft_id        | **Optional** Return runs for a specific draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |
| since           | **Optional** Only retrieve story runs that started after this time (ISO 8601 timestamp).                                         |
| until           | **Optional** Only retrieve story runs that started until this time (ISO 8601 timestamp).                                         |

```bash
curl -X GET \
 https://<tenant-domain>/api/v1/stories/<<story_id>>/runs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON Array describing the story runs for a story.

### Field description

| Parameter    | Description                             |
| ------------ | --------------------------------------- |
| guid         | Guid of the story run.                  |
| duration     | Duration of the story run.              |
| story_id     | ID of the story.                        |
| start_time   | The time the story run started.         |
| end_time     | The time the story run ended.           |
| action_count | Number of actions in the run.           |
| event_count  | Number of events in the run.            |
| story_mode   | The mode of the story `LIVE` or `TEST`. |
| draft_id     | ID of the selected draft.               |
| draft_name   | Name of the selected draft.             |

<!-- cspell:disable -->

### Sample response

```json
{
  "story_runs": [
    {
      "guid": "5466f325-0bc6-47fb-943f-41d1a2a8d6ae",
      "duration": 0,
      "story_id": 155,
      "start_time": "2023-12-13T16:19:50Z",
      "end_time": "2023-12-13T16:19:50Z",
      "action_count": 5,
      "event_count": 5,
      "story_mode": "LIVE",
      "draft_id": "1234",
      "draft_name": "Initial Draft"
    },
    {
      "guid": "c0ea6684-1cfb-4ac5-858b-79c631daa79f",
      "duration": 0,
      "story_id": 155,
      "start_time": "2023-12-13T16:16:49Z",
      "end_time": "2023-12-13T16:16:49Z",
      "action_count": 1,
      "event_count": 1,
      "story_mode": "LIVE",
      "draft_id": "1234",
      "draft_name": "Initial Draft"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/stories/<<story_id>>/runs?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Story versions

#### Create

## Description

Create a story version.

## Request

HTTP Method: **POST**

| Query Parameter | Description                                                                                                                                     |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| name            | **Optional** The story version name.                                                                                                            |
| draft_id        | **Optional** Specify the draft for which to create a version. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/versions  \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Test story version"
      }'
```

## Response

A successful request will return a JSON object describing the story version.

### Field description

| Parameter   | Description                             |
| ----------- | --------------------------------------- |
| id          | The story version ID.                   |
| name        | The story version name.                 |
| description | The story description.                  |
| timestamp   | The time the story version was created. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 885,
  "name": "API created version",
  "description": "",
  "timestamp": "2023-11-13T11:16:05Z"
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a story version.

## Request

HTTP Method: **GET**

| Path Parameter | Description                              |
| -------------- | ---------------------------------------- |
| story_id       | The ID of the story.                     |
| version_id     | The ID of the story version to retrieve. |

```bash
curl -X GET \
 https://<tenant-domain>/api/v1/stories/<<story_id>>/versions/<<version_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON object describing the story version.

### Field description

| Parameter   | Description                             |
| ----------- | --------------------------------------- |
| id          | story version id.                       |
| name        | story version name.                     |
| description | story version description.              |
| timestamp   | The time the story version was created. |
| export_file | The export file for the story version.  |

<!-- cspell:disable -->

### Sample response

```json
{
  "id": 889,
  "name": "Story version 1",
  "description": "",
  "timestamp": "2023-11-13T11:16:05Z",
  "export_file": {
    "schema_version": 18,
    "standard_lib_version": 38,
    "action_runtime_version": 4,
    "name": "Story 7",
    "description": "Example export for an empty story",
    "guid": "a177fea37b6ffd9d52fb4b27e635dbb6",
    "slug": "story_7",
    "agents": [],
    "diagram_notes": [],
    "links": [],
    "diagram_layout": "{}",
    "send_to_story_enabled": false,
    "entry_agent_guid": null,
    "exit_agent_guids": [],
    "exit_agent_guid": null,
    "api_entry_action_guids": [],
    "api_exit_action_guids": [],
    "keep_events_for": 604800,
    "reporting_status": true,
    "send_to_story_access": null,
    "story_library_metadata": {},
    "monitor_failures": false,
    "form": null,
    "synchronous_webhooks_enabled": false,
    "forms": [],
    "pages": [],
    "tags": [],
    "time_saved_unit": "minutes",
    "time_saved_value": 0,
    "origin_story_identifier": "cloud:93b6b6f3c7c8d824afc9cb6d0b32454c:a177fea37b6ffd9d52fb4b27e635dbb6",
    "integration_product": null,
    "integration_vendor": null,
    "exported_at": "2023-11-13T11:25:48Z",
    "icon": ":clipboard:",
    "integrations": []
  }
}
```

<!-- cspell:enable -->

#### Update

## Description

Update a story version.

## Request

HTTP Method: **POST**

| Query Parameter | Description                         |
| --------------- | ----------------------------------- |
| name            | The new name for the story version. |

| Path Parameter | Description                            |
| -------------- | -------------------------------------- |
| story_id       | The ID of the story.                   |
| version_id     | The ID of the story version to update. |

```bash
curl -X POST \
 https://<tenant-domain>/api/v1/stories/<<story_id>>/versions/<<version_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Updated version name"
      }'
```

## Response

A successful request will return the JSON object describing the updated story version.

### Field description

| Parameter   | Description                             |
| ----------- | --------------------------------------- |
| id          | story version id.                       |
| name        | story version name.                     |
| description | story version description.              |
| timestamp   | The time the story version was created. |

<!-- cspell:disable -->

### Sample response

```json
{
  "id": 889,
  "name": "Updated version name",
  "description": "",
  "timestamp": "2023-11-13T11:16:05Z"
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of story versions.

## Request

HTTP Method: **GET**

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

| Query Parameter | Description                                                                                                         |
| --------------- | ------------------------------------------------------------------------------------------------------------------- |
| per_page        | **Optional** Set the number of results returned per page.                                                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                 |
| draft_id        | **Optional** The ID of the draft. [About drafts](https://www.tines.com/docs/stories/change-control/#api-endpoints). |

```bash
curl -X GET \
 https://<tenant-domain>/api/v1/stories/<<story_id>>/versions \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON Array describing the story versions for a story.

### Field description

| Parameter   | Description                             |
| ----------- | --------------------------------------- |
| id          | story version id.                       |
| name        | story version name.                     |
| description | story version description.              |
| timestamp   | The time the story version was created. |

<!-- cspell:disable -->

### Sample response

```json
{
  "story_versions": [
    {
      "id": 889,
      "name": "Story version 1",
      "description": "",
      "timestamp": "2023-11-13T11:16:05Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/stories/<<story_id>>/versions?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 7
  }
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a story version.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description                            |
| -------------- | -------------------------------------- |
| story_id       | The ID of the story.                   |
| version_id     | The ID of the story version to delete. |

```bash
curl -X DELETE \
 https://<tenant-domain>/api/v1/stories/<<story_id>>/versions/<<version_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the JSON object describing the deleted story version with a `200` status code.

### Field description

| Parameter    | Description       |
| ------------ | ----------------- |
| destroyed_id | story version ID. |

<!-- cspell:disable -->

### Sample response

```json
{
  "destroyed_id": 1
}
```

<!-- cspell:enable -->

## Teams

### Create

## Description

Create a team in Tines.

## Request

HTTP Method: **POST**

| Parameter | Description    |
| --------- | -------------- |
| name      | The team name. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/teams/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "name": "Security team"
      }'
```

## Response

A successful request will return a JSON object describing the created team

### Field description

| Parameter | Description    |
| --------- | -------------- |
| id        | The team ID.   |
| name      | The team name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 1,
  "name": "Security team"
}
```

<!-- cspell:enable -->

### Get

## Description

Retrieve a single team or case group.

## Request

HTTP Method: **GET**

| Parameter | Description                     |
| --------- | ------------------------------- |
| team_id   | The ID of the team to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/teams/<<team_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified team.

### Field description

| Parameter | Description      |
| --------- | ---------------- |
| id        | The team ID.     |
| name      | The team name.   |
| groups    | The team groups. |

### Sample response

```json
{
  "id": 1,
  "name": "Security team",
  "groups": [
    {
      "id": 3,
      "name": "Tier 1"
    }
  ]
}
```

### Update

## Description

Update a team.

## Request

HTTP Method: **PUT**

| Parameter | Description                |
| --------- | -------------------------- |
| name      | The new name for the team. |

| Path Parameter | Description    |
| -------------- | -------------- |
| team_id        | ID of the team |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/teams/<<team_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "name": "Security team 1"
      }'
```

## Response

A successful request will return a JSON object describing the updated team

### Field description

| Parameter | Description    |
| --------- | -------------- |
| id        | The team ID.   |
| name      | The team name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 1,
  "name": "Security team 1"
}
```

<!-- cspell:enable -->

### List

## Description

Retrieve a list of teams.

## Request

HTTP Method: **GET**

| Query Parameter        | Description                                                                                         |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| include_personal_teams | **Optional** Include personal teams in the response Default: `false`                                |
| per_page               | **Optional** Set the number of results returned per page.                                           |
| page                   | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/teams \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing teams in the Tines tenant.

### Field description

| Parameter | Description      |
| --------- | ---------------- |
| id        | The team ID.     |
| name      | The team name.   |
| groups    | The team groups. |

### Sample response

<!-- cspell:disable -->

```json
{
  "teams": [
    {
      "id": 1,
      "name": "Security Team",
      "groups": [
        {
          "id": 3,
          "name": "Tier 1"
        }
      ]
    },
    {
      "id": 2,
      "name": "Engineering Team",
      "groups": []
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/teams?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### List members

## Description

Retrieve a list of team members.

## Request

HTTP Method: **GET**

| Path Parameter | Description    |
| -------------- | -------------- |
| team_id        | ID of the team |

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| per_page        | **Optional** Set the number of results returned per page.                                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/teams/<<team_id>>/members \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing team members.

### Field description

| Parameter           | Description                                                                   |
| ------------------- | ----------------------------------------------------------------------------- |
| id                  | User ID.                                                                      |
| email               | User email.                                                                   |
| first_name          | User first name.                                                              |
| last_name           | User last name.                                                               |
| is_admin            | Boolean flag indicating whether user is an admin.                             |
| created_at          | Timestamp describing when the user was created.                               |
| last_seen           | Timestamp describing when the user was last seen in the platform.             |
| invitation_accepted | Boolean flag indicating if the user accepted the invitation to join the team. |
| role                | The user’s role on the team.                                                  |

### Sample response

<!-- cspell:disable -->

```json
{
  "members": [
    {
      "id": 1,
      "first_name": "First name",
      "last_name": "Last name",
      "email": "user@example.com",
      "is_admin": true,
      "created_at": "2021-03-11T09:00:58Z",
      "last_seen": "2021-04-30T08:56:05Z",
      "invitation_accepted": true,
      "role": "TEAM_ADMIN"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/teams/4178/members?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

<!-- cspell:enable -->

### Remove member

## Description

Remove a user from a team

## Request

HTTP Method: **POST**

| Path Parameter | Description    |
| -------------- | -------------- |
| team_id        | ID of the team |

| Query Parameter | Description    |
| --------------- | -------------- |
| user_id         | ID of the user |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/teams/<<team_id>>/remove_member \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "user_id": 2
      }'
```

## Response

A successful request will return a 200 response

### Sample response

```json
{
  "deleted_id": 2
}
```

### Delete

## Description

Delete a team or case group.

## Request

HTTP Method: **DELETE**

| Parameter | Description                   |
| --------- | ----------------------------- |
| team_id   | The ID of the team to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/teams/<<team_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Invite member

## Description

Invite a user to join a team

## Request

HTTP Method: **POST**

| Path Parameter | Description    |
| -------------- | -------------- |
| team_id        | ID of the team |

| Query Parameter | Description                                                                                                                                            |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| email           | Email address of the user                                                                                                                              |
| user_id         | **optional** ID of an existing user in the Tines tenant                                                                                                |
| role            | **optional** Which [role](https://www.tines.com/docs/admin/teams#team-roles) the user should have on the team. Supports both default and custom roles. |

**`user_id` or `email` must be provided but not both.**

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/teams/<<team_id>>/invite_member \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "email": "user@example.com"
      }'
```

## Response

A successful request will return a JSON object representing the invited user.

### Field description

| Parameter           | Description                                                                   |
| ------------------- | ----------------------------------------------------------------------------- |
| id                  | User ID.                                                                      |
| email               | User email.                                                                   |
| first_name          | User first name.                                                              |
| last_name           | User last name.                                                               |
| is_admin            | Boolean flag indicating whether user is an admin.                             |
| created_at          | Timestamp describing when the user was created.                               |
| last_seen           | Timestamp describing when the user was last seen in the platform.             |
| invitation_accepted | Boolean flag indicating if the user accepted the invitation to join the team. |
| role                | The user’s role on the team.                                                  |

### Sample response

```json
{
  "id": 2,
  "first_name": "First name",
  "last_name": "Last name",
  "email": "user@example.com",
  "is_admin": false,
  "created_at": "2021-04-30T09:12:40Z",
  "last_seen": null,
  "invitation_accepted": false,
  "role": "VIEWER"
}
```

### Resend member invitation

## Description

Resend a team invitation to a user.

## Request

HTTP Method: **POST**

| Path Parameter | Description    |
| -------------- | -------------- |
| team_id        | ID of the team |

| Query Parameter | Description |
| --------------- | ----------- |
| user_id         | ID of user  |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/teams/<<team_id>>/resend_invitation \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "user_id": 2
      }'
```

## Response

A successful request will return a JSON object representing the invited user.

### Field description

| Parameter           | Description                                                                  |
| ------------------- | ---------------------------------------------------------------------------- |
| id                  | User ID.                                                                     |
| email               | User email.                                                                  |
| first_name          | User first name.                                                             |
| last_name           | User last name.                                                              |
| is_admin            | Boolean flag indicating whether user is an admin.                            |
| created_at          | Timestamp describing when the user was created.                              |
| last_seen           | Timestamp describing when the user was last seen in the platform.            |
| invitation_accepted | Boolean flag indicating if the user accepted the invitation to join the team |

### Sample response

```json
{
  "id": 2,
  "first_name": "First name",
  "last_name": "Last name",
  "email": "user@example.com",
  "is_admin": false,
  "created_at": "2021-04-30T09:12:40Z",
  "last_seen": null,
  "invitation_accepted": false
}
```

### Destroy static external ID

## Description

Destroy a team static external ID in Tines.

## Request

HTTP Method: **POST**

| Parameter | Description  |
| --------- | ------------ |
| team_id   | The team ID. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/teams/<<team_id>>/destroy_static_external_id \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a 200 response and the ID of the destroyed team static external ID.

```bash
12
```

## Cases

### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Create a case.

## Request

HTTP Method: **POST**

| Parameter          | Description                                                                                                                                                          |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id            | The team ID the case is within.                                                                                                                                      |
| name               | The case name.                                                                                                                                                       |
| description        | **Optional** The case description. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID.      |
| priority           | **Optional** The case priority - critical, high, medium, low or info.                                                                                                |
| status             | **Optional** The case status - open or closed.                                                                                                                       |
| author_email       | **Optional** The email of the user creating the cases.                                                                                                               |
| assignee_emails    | **Optional** An array of user email addresses to assign to the case. Case will automatically be assigned to the user associated with the API token if none provided. |
| tag_names          | **Optional** An array of tag names.                                                                                                                                  |
| record_ids         | **Optional** An array of record IDs to add to the case.                                                                                                              |
| opened_at          | **Optional** ISO 8601 Timestamp representing the date and time the case was opened at.                                                                               |
| resolved_at        | **Optional** ISO 8601 Timestamp representing the date and time the case was resolved at.                                                                             |
| metadata           | **Optional** Case related metadata represented as key-value pairs.                                                                                                   |
| team_case_buttons  | **Optional** An array of team case buttons for downstream actions associated with the case.                                                                          |
| closure_conditions | **Optional** An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case.                   |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "team_id": "team_id",
  "name": "Suspicious login detected",
  "priority": "high",
  "description": "This is a high priority case created via the API",
  "assignee_emails": [
    "jane@tines.io"
  ],
  "tag_names": [
    "login",
    "security"
  ],
  "record_ids": [
    "record_id"
  ],
  "metadata": {
    "key": "value"
  },
  "team_case_buttons": [
    {
      "button_type": "page",
      "url": "https://tenant.tines.com/pages/55e94b9b4e26175051c0287f4259363f/",
      "label": "Isolate host and give reason",
      "button_text": "Open form"
    },
    {
      "button_type": "webhook",
      "url": "http://tenant.tines.com/webhook/e840b4e8cf60437196d9a4bcb85d2de0/61441bc05fddfa4c09924b23fcf72b91",
      "label": "Isolate host",
      "button_text": "Isolate"
    }
  ],
  "closure_conditions": [
    {
      "name": "has at least 1 assignee",
      "formula": "=SIZE(team_case.assignees) > 0"
    }
  ]
}'
```

## Response

A successful request will return a JSON object describing the created case.

### Field description

| Parameter          | Description                                                                    |
| ------------------ | ------------------------------------------------------------------------------ |
| case_id            | The case ID.                                                                   |
| name               | The case name.                                                                 |
| description        | The case description.                                                          |
| status             | The case status - open or closed.                                              |
| url                | The case URL.                                                                  |
| metadata           | Case related metadata represented as key-value pairs.                          |
| author             | An object describing the case author.                                          |
| opened_by          | An object describing the user who opened the case.                             |
| resolved_by        | An object describing the user who resolved the case.                           |
| assignees          | An array of users assigned to the case.                                        |
| tags               | An array of tags associated with the case.                                     |
| team_case_actions  | An array of team case actions taken within the case.                           |
| team               | The team the case is within - ID & name.                                       |
| team_case_buttons  | An array of team case buttons for downstream actions associated with the case. |
| linked_cases       | An array of cases linked to this case (IDs only).                              |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.      |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.    |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.     |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.     |
| records            | An array of records associated with the case.                                  |
| priority           | The case priority - critical, high, medium, low or info.                       |
| closure_conditions | An array of closure requirements for the case.                                 |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Suspicious login detected",
  "description": "This is a high priority case created via the API",
  "status": "OPEN",
  "url": "https://tenant.tines.com/team/2/cases/42",
  "metadata": {},
  "author": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "is_service_account": false
  },
  "opened_by": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "is_service_account": false
  },
  "resolved_by": null,
  "assignees": [
    {
      "id": 1,
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "is_service_account": false
    }
  ],
  "tags": [
    {
      "id": 4,
      "name": "login"
    }
  ],
  "team_case_actions": [
    {
      "id": 7508,
      "action": "CREATED",
      "value": "42",
      "user": {}
    }
  ],
  "team": {
    "id": 2,
    "name": "Team 1"
  },
  "team_case_buttons": [
    {
      "id": 8,
      "button_type": "webhook",
      "url": "http://tenant.tines.com/webhook/e840b4e8cf60437196d9a4bcb85d2de0/61441bc05fddfa4c09924b23fcf72b91",
      "label": "Isolate host",
      "button_text": "Isolate"
    },
    {
      "id": 9,
      "button_type": "page",
      "url": "https://tenant.tines.com/pages/55e94b9b4e26175051c0287f4259363f/",
      "label": "Isolate host and submit reason",
      "button_text": "Open form"
    }
  ],
  "closure_conditions": [
    {
      "name": "has at least 1 assignee",
      "formula": "=SIZE(team_case.assignees) > 0"
    }
  ]
  "linked_cases": [],
  "opened_at": "2023-12-18T22:29:22Z",
  "resolved_at": null,
  "created_at": "2023-12-18T22:29:22Z",
  "updated_at": "2023-12-18T22:29:22Z",
  "records": [
    {
      "id": 187,
      "results": [
        {
          "id": 1871,
          "name": "Story name",
          "value": "Cases API"
        }
      ]
    }
  ],
  "priority": "HIGH"
}
```

<!-- cspell:enable -->

### Create

## Description

Create a case.

## Request

HTTP Method: **POST**

| Parameter          | Description                                                                                                                                                                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id            | The team ID the case is within.                                                                                                                                                                                                                                       |
| name               | The case name.                                                                                                                                                                                                                                                        |
| description        | **Optional** The case description. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID.                                                                                                       |
| priority           | **Optional** The case priority - critical, high, medium, low or info.                                                                                                                                                                                                 |
| status             | **Optional** The case status - open or closed.                                                                                                                                                                                                                        |
| sub_status_id      | **Optional** The case sub-status ID.                                                                                                                                                                                                                                  |
| author_email       | **Optional** The email of the user creating the cases.                                                                                                                                                                                                                |
| assignee_emails    | **Optional** An array of user email addresses to assign to the case. Case will automatically be assigned to the user associated with the API token if none provided.                                                                                                  |
| tag_names          | **Optional** An array of tag names.                                                                                                                                                                                                                                   |
| opened_at          | **Optional** ISO 8601 Timestamp representing the date and time the case was opened at.                                                                                                                                                                                |
| resolved_at        | **Optional** ISO 8601 Timestamp representing the date and time the case was resolved at.                                                                                                                                                                              |
| metadata           | **Optional** Case related metadata represented as key-value pairs.                                                                                                                                                                                                    |
| closure_conditions | **Optional** An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. <br><br> **N.B.** The formula key should be passed as a raw formula wrapped in strings as opposed to a pill value. |
| field_values       | **Optional** An object containing a key-value pair of case input IDs and values for the case. Case input IDs provided must exist within the team for the field to be successfully added to the case.                                                                  |
| tasks              | **Optional** An array of task objects to create with the case. Each task object should contain a description and optionally assignee_ids or assignee_emails.                                                                                                          |
| blocks             | **Optional** An array of block objects to create with the case. See [block parameters](#block-parameters) below.                                                                                                                                                      |

### Block parameters

Each object in the `blocks` array accepts:

| Parameter         | Description                                                                                                                       |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| title             | The title of the block.                                                                                                           |
| block_type        | The type of block to create - options: note, file, linked_cases, metadata, closure_conditions, case_action, block_group and html. |
| elements          | An array of elements to add to the block. Required but may be empty for block types that do not support elements.                 |
| hidden            | **Optional** A boolean (true or false).                                                                                           |
| guid              | **Optional** A client-generated identifier for the block, used for referencing in `parent_block_guid`.                            |
| parent_block_guid | **Optional** The `guid` of a block_group block within the same request to nest this block inside.                                 |

#### Note element parameters

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                                            |
| note_type | The type of note - "html" for html blocks and "text" for any others.                                |
| color     | **Optional** The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

#### File element parameters

| Parameter     | Description                                    |
| ------------- | ---------------------------------------------- |
| filename      | The name of the file.                          |
| file_contents | The base64-encoded contents of the file.       |
| annotation    | **Optional** The annotation text for the file. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "team_id": "team_id",
  "name": "Suspicious login detected",
  "priority": "high",
  "description": "This is a high priority case created via the API",
  "assignee_emails": [
    "jane@tines.io"
  ],
  "tag_names": [
    "login",
    "security"
  ],
  "metadata": {
    "key": "value"
  },
  "closure_conditions": [
    {
      "name": "has at least 1 assignee",
      "formula": "=SIZE(team_case.assignees) > 0"
    }
  ],
  "field_values": {
    "1": "Windows"
  },
  "tasks": [
    {
      "description": "Review security logs",
      "assignee_ids": [123, 456]
    },
    {
      "description": "Contact affected users"
    }
  ],
  "blocks": [
    {
      "title": "Investigation notes",
      "block_type": "note",
      "elements": [
        {
          "content": "Initial triage completed - suspicious IP identified.",
          "note_type": "text",
          "color": "gold"
        }
      ]
    },
    {
      "title": "Evidence",
      "block_type": "block_group",
      "guid": "evidence-group",
      "elements": []
    },
    {
      "title": "Log files",
      "block_type": "file",
      "parent_block_guid": "evidence-group",
      "elements": [
        {
          "filename": "auth-logs.txt",
          "file_contents": "SGVsbG8gV29ybGQ="
        }
      ]
    }
  ]
}'
```

## Response

A successful request will return a JSON object describing the created case.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The ID of the case.                                                                                                                   |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| sub_status         | An object describing the case sub-status.                                                                                             |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| activities         | An array of team case actions taken within the case.                                                                                  |
| activities_meta    | Metadata defining pagination params required to fetch additional activities for the case.                                             |
| team               | The team the case is within - ID & name.                                                                                              |
| actions            | An array of case actions for downstream actions associated with the case.                                                             |
| linked_cases       | An array of cases linked to this case - ID & name.                                                                                    |
| sla                | An object describing the case SLA status. Times are in seconds.                                                                       |
| slas               | An array of SLAs for the case. Times are in seconds.                                                                                  |
| fields             | An array of field values for the case.                                                                                                |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case, grouped by record type.                                                                 |
| records_meta       | Metadata defining pagination params required to fetch additional records for the case.                                                |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |
| blocks             | An array of block objects specifying the block title, slug, type, position, visibility, group, and element ids.                       |
| tasks              | An array of tasks associated with the case.                                                                                           |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 1,
  "name": "Case 1..25",
  "description": null,
  "status": "OPEN",
  "sub_status": {
    "id": 1,
    "name": "To do"
  },
  "priority": "LOW",
  "url": "http://<tenant-domain>/team/276/cases/1",
  "metadata": {
    "foo": "bar"
  },
  "author": {
    "user_id": "168",
    "first_name": "Example",
    "last_name": "Person",
    "email": "person_2@example.com",
    "avatar_url": "",
    "is_service_account": false
  },
  "opened_by": null,
  "resolved_by": null,
  "assignees": [
    {
      "user_id": "176",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_10@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "178",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_12@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "180",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_14@example.com",
      "avatar_url": "",
      "is_service_account": false
    }
  ],
  "tags": [
    {
      "id": 7,
      "name": "Tag 1"
    },
    {
      "id": 8,
      "name": "Tag 2"
    },
    {
      "id": 9,
      "name": "Tag 3"
    }
  ],
  "sla": {
    "remaining_time_seconds": 0,
    "current_time_seconds": 0,
    "percent_elapsed": 0,
    "exceeded": false,
    "sla_type": "completion"
  },
  "slas": [
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 0,
      "exceeded": false,
      "sla_type": "completion"
    },
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 100,
      "exceeded": true,
      "sla_type": "response"
    }
  ],
  "fields": [
    {
      "id": 1,
      "value": "Windows 11",
      "case_input": {
        "id": 1,
        "key": "operating_system",
        "name": "Operating system",
        "validation_type": "none",
        "validation_options": {}
      }
    }
  ],
  "activities": [
    {
      "id": 109,
      "activity_type": "CREATED",
      "value": "115",
      "user": {
        "user_id": "168",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_2@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 111,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "170",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_4@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 113,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "172",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_6@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    }
  ],
  "team": {
    "id": 276,
    "name": "Test team 1"
  },
  "linked_cases": [{ "case_id": 1, "name": "Case 1" }],
  "closure_conditions": [
    {
      "formula": "=SIZE(team_case.assignees) > 2",
      "name": "Number of assignees"
    }
  ],
  "opened_at": "2024-03-25T15:40:39Z",
  "resolved_at": null,
  "created_at": "2024-03-25T15:40:39Z",
  "updated_at": "2024-03-25T15:40:39Z",
  "records": [
    {
      "record_type_id": 55,
      "record_type_name": "Record type 2",
      "record_type_record_results": [
        {
          "id": 59,
          "results": [
            {
              "id": 59110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 59109,
              "name": "Story name",
              "value": "Alert investigation"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 58,
          "results": [
            {
              "id": 58110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 58109,
              "name": "Story name",
              "value": "Alert investigation #1"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    },
    {
      "record_type_id": 54,
      "record_type_name": "Record type 1",
      "record_type_record_results": [
        {
          "id": 56,
          "results": [
            {
              "id": 56108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 56107,
              "name": "Story name",
              "value": "Alert investigation #2"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 55,
          "results": [
            {
              "id": 55108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 55107,
              "name": "Story name",
              "value": "Alert investigation #3"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    }
  ],
  "actions": [
    {
      "id": 7,
      "url": "http://example.com",
      "label": "Hello, world!",
      "action_type": "page"
    }
  ],
  "blocks": [
    {
      "id": 1,
      "title": "Notes",
      "slug": "blk_notes",
      "block_type": "note",
      "position": 1,
      "hidden": false,
      "block_group_id": null,
      "elements": [
        { "element_id": 54, "id": 54 },
        { "element_id": 674, "id": 674 },
        { "element_id": 524, "id": 524 }
      ]
    },
    {
      "id": 3,
      "title": "Investigation",
      "slug": "blk_investigation",
      "block_type": "block_group",
      "position": 2,
      "hidden": false,
      "block_group_id": null,
      "elements": []
    },
    {
      "id": 2,
      "title": "Additional notes",
      "slug": "blk_additional_notes",
      "block_type": "note",
      "position": 3,
      "hidden": true,
      "block_group_id": 3,
      "elements": [
        { "element_id": 235, "id": 235 },
        { "element_id": 342, "id": 342 },
        { "element_id": 744, "id": 744 }
      ]
    }
  ],
  "tasks": [
    {
      "id": 1,
      "description": "Review security logs",
      "completed": false,
      "assignees": [
        {
          "id": "123"
        },
        {
          "id": "456"
        }
      ],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 2,
      "description": "Contact affected users",
      "completed": false,
      "assignees": [],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    }
  ],
  "activities_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/activities?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  },
  "records_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/records?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Get

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a single case.

## Request

HTTP Method: **GET**

| Parameter | Description                     |
| --------- | ------------------------------- |
| case_id   | The ID of the case to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified case.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The case ID.                                                                                                                          |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| team_case_actions  | An array of team case actions taken within the case.                                                                                  |
| team               | The team the case is within - ID & name.                                                                                              |
| team_case_buttons  | An array of team case buttons for downstream actions associated with the case.                                                        |
| linked_cases       | An array of cases linked to this case (IDs only).                                                                                     |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case.                                                                                         |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Suspicious login detected",
  "description": "This is a high priority case created via the API",
  "status": "OPEN",
  "url": "https://tenant.tines.com/team/2/cases/42",
  "metadata": {},
  "author": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "is_service_account": false
  },
  "opened_by": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "is_service_account": false
  },
  "resolved_by": null,
  "assignees": [
    {
      "id": 1,
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "is_service_account": false
    }
  ],
  "tags": [
    {
      "id": 4,
      "name": "login"
    }
  ],
  "team_case_actions": [
    {
      "id": 7508,
      "action": "CREATED",
      "value": "42",
      "user": {}
    }
  ],
  "team": {
    "id": 2,
    "name": "Team 1"
  },
  "team_case_buttons": [
    {
      "id": 8,
      "button_type": "webhook",
      "url": "http://tenant.tines.com/webhook/e840b4e8cf60437196d9a4bcb85d2de0/61441bc05fddfa4c09924b23fcf72b91",
      "label": "Isolate host",
      "button_text": "Isolate"
    },
    {
      "id": 9,
      "button_type": "page",
      "url": "https://tenant.tines.com/pages/55e94b9b4e26175051c0287f4259363f/",
      "label": "Isolate host and submit reason",
      "button_text": "Open form"
    }
  ],
  "linked_cases": [],
  "opened_at": "2023-12-18T22:29:22Z",
  "resolved_at": null,
  "created_at": "2023-12-18T22:29:22Z",
  "updated_at": "2023-12-18T22:29:22Z",
  "records": [
    {
      "id": 187,
      "results": [
        {
          "id": 1871,
          "name": "Story name",
          "value": "Cases API"
        }
      ]
    }
  ],
  "priority": "HIGH",
  "closure_conditions": [
    {
      "name": "has at least 1 assignee",
      "formula": "=SIZE(team_case.assignees) > 0"
    }
  ]
}
```

<!-- cspell:enable -->

### Get

## Description

Retrieve a single case.

## Request

HTTP Method: **GET**

| Parameter | Description                     |
| --------- | ------------------------------- |
| case_id   | The ID of the case to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified case.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The ID of the case.                                                                                                                   |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| sub_status         | An object describing the case sub-status.                                                                                             |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| activities         | An array of team case actions taken within the case.                                                                                  |
| activities_meta    | Metadata defining pagination params required to fetch additional activities for the case.                                             |
| team               | The team the case is within - ID & name.                                                                                              |
| actions            | An array of case actions for downstream actions associated with the case.                                                             |
| fields             | An array of field values for the case.                                                                                                |
| linked_cases       | An array of cases linked to this case - ID & name.                                                                                    |
| sla                | An object describing the case SLA status. Times are in seconds.                                                                       |
| slas               | An an array of SLAs for the case. Times are in seconds.                                                                               |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case, grouped by record type.                                                                 |
| records_meta       | Metadata defining pagination params required to fetch additional records for the case.                                                |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |
| blocks             | An array of block objects specifying the block title, slug, type, position, visibility, group, and element ids.                       |
| tasks              | An array of tasks associated with the case.                                                                                           |

#### Task object fields

| Parameter   | Description                                                                |
| ----------- | -------------------------------------------------------------------------- |
| id          | The task ID.                                                               |
| description | The task description.                                                      |
| completed   | Boolean indicating whether the task is completed.                          |
| assignees   | An array of users assigned to the task.                                    |
| created_at  | ISO 8601 Timestamp representing the date and time the task was created at. |
| updated_at  | ISO 8601 Timestamp representing the date and time the task was updated at. |

#### Task assignee object fields

| Parameter  | Description            |
| ---------- | ---------------------- |
| id         | The user ID.           |
| email      | The user email.        |
| first_name | The user's first name. |
| last_name  | The user's last name.  |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 1,
  "name": "Case 1..25",
  "description": null,
  "status": "OPEN",
  "sub_status": {
    "id": 1,
    "name": "To do"
  },
  "priority": "LOW",
  "url": "http://<tenant-domain>/team/276/cases/1",
  "metadata": {
    "foo": "bar"
  },
  "author": {
    "user_id": "168",
    "first_name": "Example",
    "last_name": "Person",
    "email": "person_2@example.com",
    "avatar_url": "",
    "is_service_account": false
  },
  "opened_by": null,
  "resolved_by": null,
  "assignees": [
    {
      "user_id": "176",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_10@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "178",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_12@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "180",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_14@example.com",
      "avatar_url": "",
      "is_service_account": false
    }
  ],
  "tags": [
    {
      "id": 7,
      "name": "Tag 1"
    },
    {
      "id": 8,
      "name": "Tag 2"
    },
    {
      "id": 9,
      "name": "Tag 3"
    }
  ],
  "sla": {
    "remaining_time_seconds": 0,
    "current_time_seconds": 0,
    "percent_elapsed": 0,
    "exceeded": false,
    "sla_type": "completion"
  },
  "slas": [
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 0,
      "exceeded": false,
      "sla_type": "completion"
    },
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 100,
      "exceeded": true,
      "sla_type": "response"
    }
  ],
  "fields": [
    {
      "id": 1,
      "value": "Windows 11",
      "case_input": {
        "id": 1,
        "key": "operating_system",
        "name": "Operating system",
        "validation_type": "none",
        "validation_options": {}
      }
    }
  ],
  "activities": [
    {
      "id": 109,
      "activity_type": "CREATED",
      "value": "115",
      "user": {
        "user_id": "168",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_2@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 111,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "170",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_4@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 113,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "172",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_6@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    }
  ],
  "team": {
    "id": 276,
    "name": "Test team 1"
  },
  "linked_cases": [{ "case_id": 1, "name": "Case 1" }],
  "closure_conditions": [
    {
      "formula": "=SIZE(team_case.assignees) > 2",
      "name": "Number of assignees"
    }
  ],
  "opened_at": "2024-03-25T15:40:39Z",
  "resolved_at": null,
  "created_at": "2024-03-25T15:40:39Z",
  "updated_at": "2024-03-25T15:40:39Z",
  "records": [
    {
      "record_type_id": 55,
      "record_type_name": "Record type 2",
      "record_type_record_results": [
        {
          "id": 59,
          "results": [
            {
              "id": 59110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 59109,
              "name": "Story name",
              "value": "Alert investigation"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 58,
          "results": [
            {
              "id": 58110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 58109,
              "name": "Story name",
              "value": "Alert investigation #1"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    },
    {
      "record_type_id": 54,
      "record_type_name": "Record type 1",
      "record_type_record_results": [
        {
          "id": 56,
          "results": [
            {
              "id": 56108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 56107,
              "name": "Story name",
              "value": "Alert investigation #2"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 55,
          "results": [
            {
              "id": 55108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 55107,
              "name": "Story name",
              "value": "Alert investigation #3"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    }
  ],
  "actions": [
    {
      "id": 7,
      "url": "http://example.com",
      "label": "Hello, world!",
      "action_type": "page"
    }
  ],
  "blocks": [
    {
      "id": 1,
      "title": "Notes",
      "slug": "blk_notes",
      "block_type": "note",
      "position": 1,
      "hidden": false,
      "block_group_id": null,
      "elements": [
        { "element_id": 54, "id": 54 },
        { "element_id": 674, "id": 674 },
        { "element_id": 524, "id": 524 }
      ]
    },
    {
      "id": 2,
      "title": "Additional notes",
      "slug": "blk_additional_notes",
      "block_type": "note",
      "position": 2,
      "hidden": false,
      "block_group_id": null,
      "elements": [
        { "element_id": 235, "id": 235 },
        { "element_id": 342, "id": 342 },
        { "element_id": 744, "id": 744 }
      ]
    }
  ],
  "tasks": [
    {
      "id": 1,
      "description": "Review security logs",
      "completed": false,
      "assignees": [
        {
          "id": "123",
          "email": "user1@example.com",
          "first_name": "John",
          "last_name": "Doe"
        },
        {
          "id": "456",
          "email": "user2@example.com",
          "first_name": "Jane",
          "last_name": "Smith"
        }
      ],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 2,
      "description": "Contact affected users",
      "completed": true,
      "assignees": [
        {
          "id": "789",
          "email": "user3@example.com",
          "first_name": "Bob",
          "last_name": "Johnson"
        }
      ],
      "created_at": "2024-03-25T15:30:20Z",
      "updated_at": "2024-03-25T16:15:45Z"
    }
  ],
  "activities_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/activities?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  },
  "records_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/records?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Update

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Update a case.

## Request

HTTP Method: **PUT**

| Parameter              | Description                                                                                                                                        |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                   | **Optional** The case name.                                                                                                                        |
| description            | **Optional** The case description.                                                                                                                 |
| priority               | **Optional** The case priority - critical, high, medium, low or info.                                                                              |
| status                 | **Optional** The case status - open or closed. A case re-opened email will be sent to case assignees upon reopening a case.                        |
| add_assignee_emails    | **Optional** An array of user email addresses to assign to the case.                                                                               |
| remove_assignee_emails | **Optional** An array of user email addresses to remove from the case.                                                                             |
| add_tag_names          | **Optional** An array of tag names to add to the case.                                                                                             |
| remove_tag_names       | **Optional** An array of tag names to remove from the case.                                                                                        |
| add_record_ids         | **Optional** An array of record IDs to add to the case.                                                                                            |
| remove_record_ids      | **Optional** An array of record IDs to remove from the case.                                                                                       |
| author_email           | **Optional** The email of the user updating the case.                                                                                              |
| opened_at              | **Optional** ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at            | **Optional** ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| metadata               | **Optional** Case related metadata represented as key-value pairs.                                                                                 |
| team_case_buttons      | **Optional** An array of team case buttons for downstream actions associated with the case.                                                        |
| closure_conditions     | **Optional** An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/cases/<<case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Updated case name"
      }'
```

## Response

A successful request will return a JSON object describing the updated case.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The case ID.                                                                                                                          |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| team_case_actions  | An array of team case actions taken within the case.                                                                                  |
| team               | The team the case is within - ID & name.                                                                                              |
| team_case_buttons  | An array of team case buttons for downstream actions associated with the case.                                                        |
| linked_cases       | An array of cases linked to this case (IDs only).                                                                                     |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case.                                                                                         |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Updated case name",
  "description": "This is a high priority case created via the API",
  "status": "OPEN",
  "url": "https://tenant.tines.com/team/2/cases/42",
  "metadata": {},
  "author": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "is_service_account": false
  },
  "opened_by": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "is_service_account": false
  },
  "resolved_by": null,
  "assignees": [
    {
      "id": 1,
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "is_service_account": false
    }
  ],
  "tags": [
    {
      "id": 4,
      "name": "login"
    }
  ],
  "team_case_actions": [
    {
      "id": 7508,
      "action": "CREATED",
      "value": "42",
      "user": {}
    }
  ],
  "team": {
    "id": 2,
    "name": "Team 1"
  },
  "team_case_buttons": [
    {
      "id": 8,
      "button_type": "webhook",
      "url": "http://tenant.tines.com/webhook/e840b4e8cf60437196d9a4bcb85d2de0/61441bc05fddfa4c09924b23fcf72b91",
      "label": "Isolate host",
      "button_text": "Isolate"
    },
    {
      "id": 9,
      "button_type": "page",
      "url": "https://tenant.tines.com/pages/55e94b9b4e26175051c0287f4259363f/",
      "label": "Isolate host and submit reason",
      "button_text": "Open form"
    }
  ],
  "linked_cases": [],
  "opened_at": "2023-12-18T22:29:22Z",
  "resolved_at": null,
  "created_at": "2023-12-18T22:29:22Z",
  "updated_at": "2023-12-18T22:29:22Z",
  "records": [
    {
      "id": 187,
      "results": [
        {
          "id": 1871,
          "name": "Story name",
          "value": "Cases API"
        }
      ]
    }
  ],
  "priority": "HIGH",
  "closure_conditions": [
    {
      "name": "has at least 1 assignee",
      "formula": "=SIZE(team_case.assignees) > 0"
    }
  ]
}
```

<!-- cspell:enable -->

### Update

## Description

Update a case.

## Request

HTTP Method: **PUT**

| Parameter              | Description                                                                                                                                                                                                                                                          |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                   | **Optional** The case name.                                                                                                                                                                                                                                          |
| team_id                | **Optional** The ID of the team (or group) the case belongs to.                                                                                                                                                                                                      |
| description            | **Optional** The case description. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID.                                                                                                      |
| priority               | **Optional** The case priority - critical, high, medium, low or info.                                                                                                                                                                                                |
| status                 | **Optional** The case status - open or closed.                                                                                                                                                                                                                       |
| sub_status_id          | **Optional** The case sub-status ID.                                                                                                                                                                                                                                 |
| author_email           | **Optional** The email of the user creating the cases.                                                                                                                                                                                                               |
| assignee_emails        | **Optional** An array of user email addresses to assign to the case. Case will automatically be assigned to the user associated with the API token if none provided.                                                                                                 |
| add_assignee_emails    | **Optional** An array of user email addresses to assign to the case.                                                                                                                                                                                                 |
| remove_assignee_emails | **Optional** An array of user email addresses to remove from the case.                                                                                                                                                                                               |
| add_tag_names          | **Optional** An array of tag names to add to the case.                                                                                                                                                                                                               |
| remove_tag_names       | **Optional** An array of tag names to remove from the case.                                                                                                                                                                                                          |
| opened_at              | **Optional** ISO 8601 Timestamp representing the date and time the case was opened at.                                                                                                                                                                               |
| resolved_at            | **Optional** ISO 8601 Timestamp representing the date and time the case was resolved at.                                                                                                                                                                             |
| closure_conditions     | **Optional** An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case.<br><br> **N.B.** The formula key should be passed as a raw formula wrapped in strings as opposed to a pill value. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Updated case name"
      }'
```

## Response

A successful request will return a JSON object describing the updated case.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The ID of the case.                                                                                                                   |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| sub_status         | An object describing the case sub-status.                                                                                             |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| activities         | An array of team case actions taken within the case.                                                                                  |
| activities_meta    | Metadata defining pagination params required to fetch additional activities for the case.                                             |
| team               | The team the case is within - ID & name.                                                                                              |
| actions            | An array of case actions for downstream actions associated with the case.                                                             |
| linked_cases       | An array of cases linked to this case - ID & name.                                                                                    |
| sla                | An object describing the case SLA status. Times are in seconds.                                                                       |
| slas               | An an array of SLAs for the case. Times are in seconds.                                                                               |
| fields             | An array of field values for the case.                                                                                                |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case, grouped by record type.                                                                 |
| records_meta       | Metadata defining pagination params required to fetch additional records for the case.                                                |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |
| blocks             | An array of block objects specifying the block title, slug, type, position, visibility, group, and element ids.                       |
| tasks              | An array of tasks associated with the case.                                                                                           |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 1,
  "name": "Case 1..25",
  "description": null,
  "status": "OPEN",
  "sub_status": {
    "id": 1,
    "name": "To do"
  },
  "priority": "LOW",
  "url": "http://<tenant-domain>/team/276/cases/1",
  "metadata": {
    "foo": "bar"
  },
  "author": {
    "user_id": "168",
    "first_name": "Example",
    "last_name": "Person",
    "email": "person_2@example.com",
    "avatar_url": "",
    "is_service_account": false
  },
  "opened_by": null,
  "resolved_by": null,
  "assignees": [
    {
      "user_id": "176",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_10@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "178",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_12@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "180",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_14@example.com",
      "avatar_url": "",
      "is_service_account": false
    }
  ],
  "tags": [
    {
      "id": 7,
      "name": "Tag 1"
    },
    {
      "id": 8,
      "name": "Tag 2"
    },
    {
      "id": 9,
      "name": "Tag 3"
    }
  ],
  "sla": {
    "remaining_time_seconds": 0,
    "current_time_seconds": 0,
    "percent_elapsed": 0,
    "exceeded": false,
    "sla_type": "completion"
  },
  "slas": [
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 0,
      "exceeded": false,
      "sla_type": "completion"
    },
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 100,
      "exceeded": true,
      "sla_type": "response"
    }
  ],
  "fields": [
    {
      "id": 1,
      "value": "Windows 11",
      "case_input": {
        "id": 1,
        "key": "operating_system",
        "name": "Operating system",
        "validation_type": "none",
        "validation_options": {}
      }
    }
  ],
  "activities": [
    {
      "id": 109,
      "activity_type": "CREATED",
      "value": "115",
      "user": {
        "user_id": "168",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_2@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 111,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "170",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_4@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 113,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "172",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_6@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    }
  ],
  "team": {
    "id": 276,
    "name": "Test team 1"
  },
  "linked_cases": [{ "case_id": 1, "name": "Case 1" }],
  "closure_conditions": [
    {
      "formula": "=SIZE(team_case.assignees) > 2",
      "name": "Number of assignees"
    }
  ],
  "opened_at": "2024-03-25T15:40:39Z",
  "resolved_at": null,
  "created_at": "2024-03-25T15:40:39Z",
  "updated_at": "2024-03-25T15:40:39Z",
  "records": [
    {
      "record_type_id": 55,
      "record_type_name": "Record type 2",
      "record_type_record_results": [
        {
          "id": 59,
          "results": [
            {
              "id": 59110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 59109,
              "name": "Story name",
              "value": "Alert investigation"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 58,
          "results": [
            {
              "id": 58110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 58109,
              "name": "Story name",
              "value": "Alert investigation #1"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    },
    {
      "record_type_id": 54,
      "record_type_name": "Record type 1",
      "record_type_record_results": [
        {
          "id": 56,
          "results": [
            {
              "id": 56108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 56107,
              "name": "Story name",
              "value": "Alert investigation #2"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 55,
          "results": [
            {
              "id": 55108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 55107,
              "name": "Story name",
              "value": "Alert investigation #3"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    }
  ],
  "actions": [
    {
      "id": 7,
      "url": "http://example.com",
      "label": "Hello, world!",
      "action_type": "page"
    }
  ],
  "blocks": [
    {
      "id": 1,
      "title": "Notes",
      "slug": "blk_notes",
      "block_type": "note",
      "position": 1,
      "hidden": false,
      "block_group_id": null,
      "elements": [
        { "element_id": 54, "id": 54 },
        { "element_id": 674, "id": 674 },
        { "element_id": 524, "id": 524 }
      ]
    },
    {
      "id": 2,
      "title": "Additional notes",
      "slug": "blk_additional_notes",
      "block_type": "note",
      "position": 2,
      "hidden": false,
      "block_group_id": null,
      "elements": [
        { "element_id": 235, "id": 235 },
        { "element_id": 342, "id": 342 },
        { "element_id": 744, "id": 744 }
      ]
    }
  ],
  "tasks": [
    {
      "id": 1,
      "description": "Review security logs",
      "completed": false,
      "assignees": [
        {
          "id": "123"
        },
        {
          "id": "456"
        }
      ],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 2,
      "description": "Contact affected users",
      "completed": false,
      "assignees": [],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    }
  ],
  "activities_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/activities?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  },
  "records_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/records?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### List

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a list of cases.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** Retrieve cases for the specified team.                                                 |
| filters         | **Optional** Object specifying case filters (see the table below).                                  |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 50.            |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

Supported parameters in `filters`

| `filters` parameters | Description                                                                                                                           |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| assigned_to_me       | **Optional** A boolean (true or false).                                                                                               |
| assignees            | **Optional** An array of user IDs.                                                                                                    |
| assignee_emails      | **Optional** An array of user email addresses.                                                                                        |
| start_date           | **Optional** A timestamp (ISO 8601 format). End date will default to today if not provided.                                           |
| end_date             | **Optional** A timestamp (ISO 8601 format).                                                                                           |
| search               | **Optional** A string that searches against case name, description and ID.                                                            |
| or_tags              | **Optional** An array of tag names that are OR'd by.                                                                                  |
| and_tags             | **Optional** An array of tag names to AND'd by.                                                                                       |
| tags                 | **Optional** An array of tag names (behaves the same as `and_tags`).                                                                  |
| records              | **Optional** A string array of record result values. Cases containing a record matching any of the results provided will be returned. |
| status               | **Optional** `OPEN` or `CLOSED`.                                                                                                      |
| priority             | **Optional** `CRITICAL`, `HIGH`, `MEDIUM`, `LOW` or `INFO`                                                                            |
| metadata             | **Optional** An object of metadata as key-value pairs                                                                                 |
| author_emails        | **Optional** An array of author emails.                                                                                               |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the cases the requesting token has access to.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The case ID.                                                                                                                          |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| team_case_actions  | An array of team case actions taken within the case.                                                                                  |
| team               | The team the case is within - ID & name.                                                                                              |
| team_case_buttons  | An array of team case buttons for downstream actions associated with the case.                                                        |
| linked_cases       | An array of cases linked to this case (IDs only).                                                                                     |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case.                                                                                         |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |

### Sample response

<!-- cspell:disable -->

```json
"cases": [
  {
    "case_id": 42,
    "name": "Suspicious login detected",
    "description": "This is a high priority case created via the API",
    "status": "OPEN",
    "url": "https://tenant.tines.com/team/2/cases/42",
    "metadata": {},
    "author": {
      "id": 1,
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "is_service_account": false
    },
    "opened_by": {
      "id": 1,
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "is_service_account": false
    },
    "resolved_by": null,
    "assignees": [
      {
        "id": 1,
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@tines.io",
        "is_service_account": false
      }
    ],
    "tags": [
      {
        "id":4,
        "name": "login"
      }
    ],
    "team_case_actions": [
      {
        "id": 7508,
        "action": "CREATED",
        "value": "42",
        "user": {}
      }
    ],
    "team": {
      "id": 2,
      "name": "Team 1"
    },
    "team_case_buttons": [
      {
        "id": 8,
        "button_type": "webhook",
        "url": "http://tenant.tines.com/webhook/e840b4e8cf60437196d9a4bcb85d2de0/61441bc05fddfa4c09924b23fcf72b91",
        "label": "Isolate host"
      },
          {
        "id": 9,
        "button_type": "page",
        "url": "https://tenant.tines.com/pages/55e94b9b4e26175051c0287f4259363f/",
        "label": "Isolate host and submit reason"
      }
    ],
    "linked_cases": [],
    "opened_at": "2023-12-18T22:29:22Z",
    "resolved_at":null,
    "created_at": "2023-12-18T22:29:22Z" ,
    "updated_at": "2023-12-18T22:29:22Z",
    "records": [
      {
        "id": 187,
        "results": [
          {
            "id": 1871,
            "name": "Story name",
            "value": "Cases API"
          },
        ]
      }
    ],
    "priority": "HIGH",
    "closure_conditions": [
      {
        "name": "has at least 1 assignee",
        "formula": "=SIZE(team_case.assignees) > 0"
      }
    ]
  }
],
"meta": {
  "current_page": "https://<tenant-domain>/api/v1/cases?per_page=20&page=1",
  "previous_page": null,
  "next_page": null,
  "next_page_number": null,
  "per_page": 20,
  "pages": 1,
  "count": 1
}
```

<!-- cspell:enable -->

### List

## Description

Retrieve a list of cases.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** Retrieve cases for the specified team.                                                                                                     |
| filters         | **Optional** Object specifying case filters (see the table below).                                                                                      |
| order           | **Optional** `RECENTLY_EDITED`, `LEAST_RECENTLY_EDITED`, `PRIORITY_ASC`, `PRIORITY_DESC`, `OPENED_ASC`, `OPENED_DESC`, `CREATED_ASC` or `CREATED_DESC`. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 500.                                                               |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                                     |

Supported parameters in `filters`

| `filters` parameters      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Assignee filters**      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| assignees                 | **Optional** A single user ID, email address, the string `"unassigned"`, or an array of these values. Cases matching any of the specified assignees will be returned. Use `"unassigned"` to filter for cases with no assignees.                                                                                                                                                                                                                                                                                                                             |
| and_assignees             | **Optional** A single user ID or an array of user IDs. Cases must have all specified assignees (AND logic).                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| exclude_assignees         | **Optional** A single user ID or an array of user IDs to exclude. Cases with any of the specified assignees will be excluded.                                                                                                                                                                                                                                                                                                                                                                                                                               |
| assigned_to_me            | **Optional** A boolean (true or false). Filter cases assigned to the current user.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| assignee_emails           | **Optional** A single email address or an array of user email addresses. **Deprecated**: Use `assignees` with email addresses instead.                                                                                                                                                                                                                                                                                                                                                                                                                      |
| unassigned                | **Optional** A boolean (true or false). **Deprecated**: Use `assignees: ["unassigned"]` instead.                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| **Author filters**        |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| authors                   | **Optional** A single author user ID, email address, the string `"tines"`, or an array of these values. Cases created by any of the specified authors will be returned. Use `"tines"` to filter for cases created via automation.                                                                                                                                                                                                                                                                                                                           |
| exclude_authors           | **Optional** A single author user ID or an array of author user IDs to exclude. Cases created by any of the specified authors will be excluded.                                                                                                                                                                                                                                                                                                                                                                                                             |
| author_emails             | **Optional** A single email address or an array of author emails. **Deprecated**: Use `authors` with email addresses instead.                                                                                                                                                                                                                                                                                                                                                                                                                               |
| **Tag filters**           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| tags                      | **Optional** A single tag name or an array of tag names (behaves the same as `and_tags`). Cases must have all specified tags.                                                                                                                                                                                                                                                                                                                                                                                                                               |
| and_tags                  | **Optional** A single tag name or an array of tag names to AND'd by. Cases must have all specified tags.                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| or_tags                   | **Optional** A single tag name or an array of tag names that are OR'd by. Cases matching any of the specified tags will be returned.                                                                                                                                                                                                                                                                                                                                                                                                                        |
| exclude_tags              | **Optional** A single tag name or an array of tag names to exclude by. Cases with any of the specified tags will be excluded.                                                                                                                                                                                                                                                                                                                                                                                                                               |
| **Priority filters**      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| priority                  | **Optional** A single priority or an array of priorities. Valid values: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, `INFO`. Cases matching any of the specified priorities will be returned.                                                                                                                                                                                                                                                                                                                                                                       |
| exclude_priorities        | **Optional** A single priority or an array of priorities to exclude. Valid values: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, `INFO`.                                                                                                                                                                                                                                                                                                                                                                                                                             |
| **Status filters**        |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| status                    | **Optional** `OPEN` or `CLOSED`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| sub_status_names          | **Optional** A single sub status name or an array of sub status names. Cases matching any of the specified sub statuses will be returned.                                                                                                                                                                                                                                                                                                                                                                                                                   |
| exclude_sub_status_names  | **Optional** A single sub status name or an array of sub status names to exclude.                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| **Metadata filters**      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| metadata                  | **Optional** A single metadata object or an array of metadata objects. Each metadata object contains:<br/>• `key`: The metadata key<br/>• `value`: The metadata value. Uses AND logic (same as `and_metadata`).                                                                                                                                                                                                                                                                                                                                             |
| and_metadata              | **Optional** A single metadata object or an array of metadata objects using AND logic. Each metadata object contains:<br/>• `key`: The metadata key<br/>• `value`: The metadata value. Cases must match all specified metadata entries.                                                                                                                                                                                                                                                                                                                     |
| or_metadata               | **Optional** A single metadata object or an array of metadata objects using OR logic. Each metadata object contains:<br/>• `key`: The metadata key<br/>• `value`: The metadata value. Cases matching any of the specified metadata entries will be returned.                                                                                                                                                                                                                                                                                                |
| **Field filters**         |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| fields                    | **Optional** A single field object or an array of field objects to filter by case input values. Each field object contains:<br/>• `key`: The slug of the case input field (e.g., `<<INFO.cases.inputs.host_name.slug>>` for a "Host name" field)<br/>• `value`: The value to search for. Uses AND logic (same as `and_fields`).                                                                                                                                                                                                                             |
| and_fields                | **Optional** A single field object or an array of field objects to filter by case input values using AND logic. Each field object contains:<br/>• `key`: The slug of the case input field<br/>• `value`: The value to search for. Cases must match all specified fields.                                                                                                                                                                                                                                                                                    |
| or_fields                 | **Optional** A single field object or an array of field objects to filter by case input values using OR logic. Each field object contains:<br/>• `key`: The slug of the case input field<br/>• `value`: The value to search for. Cases matching any of the specified fields will be returned.                                                                                                                                                                                                                                                               |
| **Date filters**          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| start_date                | **Optional** A timestamp (ISO 8601 format). Date-only strings (YYYY-MM-DD) default to start of day. Filters cases created on or after this date.                                                                                                                                                                                                                                                                                                                                                                                                            |
| end_date                  | **Optional** A timestamp (ISO 8601 format). Date-only strings (YYYY-MM-DD) default to end of day. Filters cases created before or on this date.                                                                                                                                                                                                                                                                                                                                                                                                             |
| relative_date             | **Optional** A string specifying a relative date range. Valid values:<br/>• `"today"` - Cases created today<br/>• `"yesterday"` - Cases created yesterday<br/>• `"X days"` or `"X day"` - Cases created in the last X days (e.g., `"7 days"`)<br/>• `"X weeks"` or `"X week"` - Cases created in the last X weeks (e.g., `"2 weeks"`)<br/>• `"X months"` or `"X month"` - Cases created in the last X months (e.g., `"1 month"`)<br/>• `"X years"` or `"X year"` - Cases created in the last X years (e.g., `"1 year"`)<br/>Filters cases by creation date. |
| time_range                | **Optional** A string specifying a time range filter in 24-hour format: `"HH:MM-HH:MM"` (e.g., `"09:00-17:00"`). If the start time is greater than the end time (e.g., `"22:00-06:00"`), it spans midnight. Filters cases by creation time within the specified range.                                                                                                                                                                                                                                                                                      |
| resolved_at_start_date    | **Optional** A timestamp (ISO 8601 format). Date-only strings (YYYY-MM-DD) default to start of day. Filters cases resolved on or after this date.                                                                                                                                                                                                                                                                                                                                                                                                           |
| resolved_at_end_date      | **Optional** A timestamp (ISO 8601 format). Date-only strings (YYYY-MM-DD) default to end of day. Filters cases resolved before or on this date.                                                                                                                                                                                                                                                                                                                                                                                                            |
| resolved_at_relative_date | **Optional** A string specifying a relative date range for resolved date. Valid values:<br/>• `"today"` - Cases resolved today<br/>• `"yesterday"` - Cases resolved yesterday<br/>• `"X days"` or `"X day"` - Cases resolved in the last X days (e.g., `"7 days"`)<br/>• `"X weeks"` or `"X week"` - Cases resolved in the last X weeks<br/>• `"X months"` or `"X month"` - Cases resolved in the last X months<br/>• `"X years"` or `"X year"` - Cases resolved in the last X years                                                                        |
| updated_at_start_date     | **Optional** A timestamp (ISO 8601 format). Date-only strings (YYYY-MM-DD) default to start of day. Filters cases updated on or after this date.                                                                                                                                                                                                                                                                                                                                                                                                            |
| updated_at_end_date       | **Optional** A timestamp (ISO 8601 format). Date-only strings (YYYY-MM-DD) default to end of day. Filters cases updated before or on this date.                                                                                                                                                                                                                                                                                                                                                                                                             |
| updated_at_relative_date  | **Optional** A string specifying a relative date range for updated date. Valid values:<br/>• `"today"` - Cases updated today<br/>• `"yesterday"` - Cases updated yesterday<br/>• `"X days"` or `"X day"` - Cases updated in the last X days (e.g., `"7 days"`)<br/>• `"X weeks"` or `"X week"` - Cases updated in the last X weeks<br/>• `"X months"` or `"X month"` - Cases updated in the last X months<br/>• `"X years"` or `"X year"` - Cases updated in the last X years                                                                               |
| **SLA filters**           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| sla_status                | **Optional** A single SLA status or an array of SLA statuses. Valid values: `warning`, `exceeded`. Cases matching any of the specified SLA statuses will be returned (OR logic).                                                                                                                                                                                                                                                                                                                                                                            |
| and_sla_status            | **Optional** A single SLA status or an array of SLA statuses. Valid values: `warning`, `exceeded`. Cases must match all specified SLA statuses (AND logic).                                                                                                                                                                                                                                                                                                                                                                                                 |
| exclude_sla_status        | **Optional** A single SLA status or an array of SLA statuses to exclude. Valid values: `warning`, `exceeded`. Cases with any of the specified SLA statuses will be excluded.                                                                                                                                                                                                                                                                                                                                                                                |
| sla_type                  | **Optional** A single SLA type or an array of SLA types. Valid values: `resolution`, `response`. Cases matching any of the specified SLA types will be returned (OR logic).                                                                                                                                                                                                                                                                                                                                                                                 |
| and_sla_type              | **Optional** A single SLA type or an array of SLA types. Valid values: `resolution`, `response`. Cases must match all specified SLA types (AND logic).                                                                                                                                                                                                                                                                                                                                                                                                      |
| exclude_sla_type          | **Optional** A single SLA type or an array of SLA types to exclude. Valid values: `resolution`, `response`. Cases with any of the specified SLA types will be excluded.                                                                                                                                                                                                                                                                                                                                                                                     |
| sla                       | **Optional** A single SLA status or an array of SLA statuses. Valid values: `warning`, `exceeded`. Cases matching any of the specified SLA statuses will be returned (OR logic). **Deprecated**: Use `sla_status` instead.                                                                                                                                                                                                                                                                                                                                  |
| **Text search filters**   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| search                    | **Optional** A string that searches against case name, description and ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| name                      | **Optional** A string that searches against case name.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| comment                   | **Optional** A string that searches against case comments.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| note                      | **Optional** A string that searches against case notes.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| **Other filters**         |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| case_ids                  | **Optional** A single case ID or an array of case IDs.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| child_team_ids            | **Optional** A single child team ID or an array of child team IDs. Filter cases belonging to the specified teams.                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| records                   | **Optional** A single record result value or an array of record result values (strings). Cases containing a record matching any of the results provided will be returned. Note: Large text record fields are not queryable in this instance.                                                                                                                                                                                                                                                                                                                |
| subscribed                | **Optional** A boolean (true or false). Filter cases the current user is subscribed to.                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| my_open_tasks             | **Optional** A boolean (true or false). Filter cases that have open tasks assigned to the current user.                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases \
  -H 'content-type:  application/json' \
  -H 'Authorization:  Bearer <<CREDENTIAL.tines_api_key>>'
```

### Example request with search filters

Sample request params:

```json
{
  "team_id": "1",
  "filters": {
    "search": "Sample search string",
    "status": "OPEN",
    "priority": ["HIGH", "CRITICAL"],
    "fields": [
      {
        "key": "company",
        "value": "Tines"
      }
    ]
  },
  "order": "OPENED_DESC",
  "per_page": "10"
}
```

Example request:

```bash
curl -v \
-X GET \
--location \
"https://<tenant-domain>/api/v2/cases/?order=OPENED_DESC&per_page=10&team_id=1" \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
--data '{
    "filters": {
      "status": "OPEN",
      "priority": ["HIGH", "CRITICAL"],
      "fields": [
          {
              "key": "version",
              "value": "2.0"
          }
      ]
    }
}'
```

## Response

A successful request will return a JSON object containing the cases the requesting token has access to.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The ID of the case.                                                                                                                   |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| sub_status         | An object describing the case sub-status.                                                                                             |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| fields             | An array of field values for the case.                                                                                                |
| activities         | An array of team case actions taken within the case.                                                                                  |
| activities_meta    | Metadata defining pagination params required to fetch additional activities for the case.                                             |
| team               | The team the case is within - ID & name.                                                                                              |
| actions            | An array of case actions for downstream actions associated with the case.                                                             |
| linked_cases       | An array of cases linked to this case - ID & name.                                                                                    |
| sla                | An object describing the case SLA status. Times are in seconds.                                                                       |
| slas               | An array of SLAs for the case. Times are in seconds.                                                                                  |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case, grouped by record type. Each record result includes the field id, name, and value.      |
| records_meta       | Metadata defining pagination params required to fetch additional records for the case.                                                |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |
| blocks             | An array of block objects specifying the block title, slug, type, position, visibility, group, and element ids.                       |
| tasks              | An array of tasks associated with the case.                                                                                           |

### Sample response

<!-- cspell: disable -->

```json
{
  "cases": [
    {
      "case_id": 1,
      "name": "Case 1..25",
      "description": null,
      "status": "OPEN",
      "sub_status": {
        "id": 1,
        "name": "To do"
      },
      "priority": "LOW",
      "url": "http://<tenant-domain>/team/276/cases/1",
      "metadata": {
        "foo": "bar"
      },
      "author": {
        "user_id": "168",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_2@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "opened_by": null,
      "resolved_by": null,
      "assignees": [
        {
          "user_id": "176",
          "first_name": "Example",
          "last_name": "Person",
          "email": "person_10@example.com",
          "avatar_url": "",
          "is_service_account": false
        },
        {
          "user_id": "178",
          "first_name": "Example",
          "last_name": "Person",
          "email": "person_12@example.com",
          "avatar_url": "",
          "is_service_account": false
        },
        {
          "user_id": "180",
          "first_name": "Example",
          "last_name": "Person",
          "email": "person_14@example.com",
          "avatar_url": "",
          "is_service_account": false
        }
      ],
      "tags": [
        {
          "id": 7,
          "name": "Tag 1"
        },
        {
          "id": 8,
          "name": "Tag 2"
        },
        {
          "id": 9,
          "name": "Tag 3"
        }
      ],
      "sla": {
        "remaining_time_seconds": 0,
        "current_time_seconds": 0,
        "percent_elapsed": 0,
        "exceeded": false,
        "sla_type": "completion"
      },
      "slas": [
        {
          "remaining_time_seconds": 0,
          "current_time_seconds": 0,
          "percent_elapsed": 0,
          "exceeded": false,
          "sla_type": "completion"
        },
        {
          "remaining_time_seconds": 0,
          "current_time_seconds": 0,
          "percent_elapsed": 100,
          "exceeded": true,
          "sla_type": "response"
        }
      ],
      "activities": [
        {
          "id": 109,
          "activity_type": "CREATED",
          "value": "115",
          "user": {
            "user_id": "168",
            "first_name": "Example",
            "last_name": "Person",
            "email": "person_2@example.com",
            "avatar_url": "",
            "is_service_account": false
          },
          "created_at": "2024-03-25T15:40:39Z"
        },
        {
          "id": 111,
          "activity_type": "COMMENTED",
          "value": "I'm a comment",
          "user": {
            "user_id": "170",
            "first_name": "Example",
            "last_name": "Person",
            "email": "person_4@example.com",
            "avatar_url": "",
            "is_service_account": false
          },
          "created_at": "2024-03-25T15:40:39Z"
        },
        {
          "id": 113,
          "activity_type": "COMMENTED",
          "value": "I'm a comment",
          "user": {
            "user_id": "172",
            "first_name": "Example",
            "last_name": "Person",
            "email": "person_6@example.com",
            "avatar_url": "",
            "is_service_account": false
          },
          "created_at": "2024-03-25T15:40:39Z"
        }
      ],
      "team": {
        "id": 276,
        "name": "Test team 1"
      },
      "linked_cases": [{ "case_id": 1, "name": "Case 1" }],
      "closure_conditions": [
        {
          "formula": "=SIZE(team_case.assignees) > 2",
          "name": "Number of assignees"
        }
      ],
      "opened_at": "2024-03-25T15:40:39Z",
      "resolved_at": null,
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z",
      "records": [
        {
          "record_type_id": 55,
          "record_type_name": "Record type 2",
          "record_type_record_results": [
            {
              "id": 59,
              "results": [
                {
                  "id": 59110,
                  "name": "Timestamp",
                  "value": "2024-04-16T08:02:03Z"
                },
                {
                  "id": 59109,
                  "name": "Story name",
                  "value": "Alert investigation"
                }
              ],
              "created_at": "2024-04-16T08:02:03Z"
            },
            {
              "id": 58,
              "results": [
                {
                  "id": 58110,
                  "name": "Timestamp",
                  "value": "2024-04-16T08:02:03Z"
                },
                {
                  "id": 58109,
                  "name": "Story name",
                  "value": "Alert investigation #1"
                }
              ],
              "created_at": "2024-04-16T08:02:03Z"
            }
          ]
        },
        {
          "record_type_id": 54,
          "record_type_name": "Record type 1",
          "record_type_record_results": [
            {
              "id": 56,
              "results": [
                {
                  "id": 56108,
                  "name": "Timestamp",
                  "value": "2024-04-16T08:02:03Z"
                },
                {
                  "id": 56107,
                  "name": "Story name",
                  "value": "Alert investigation #2"
                }
              ],
              "created_at": "2024-04-16T08:02:03Z"
            },
            {
              "id": 55,
              "results": [
                {
                  "id": 55108,
                  "name": "Timestamp",
                  "value": "2024-04-16T08:02:03Z"
                },
                {
                  "id": 55107,
                  "name": "Story name",
                  "value": "Alert investigation #3"
                }
              ],
              "created_at": "2024-04-16T08:02:03Z"
            }
          ]
        }
      ],
      "actions": [
        {
          "id": 7,
          "url": "http://example.com",
          "label": "Hello, world!",
          "action_type": "page"
        }
      ],
      "fields": [
        {
          "id": 1,
          "value": "Tines",
          "case_input": {
            "id": 1,
            "key": "company",
            "name": "Company",
            "validation_type": "none",
            "validation_options": {}
          }
        }
      ],
      "blocks": [
        {
          "id": 1,
          "title": "Notes",
          "slug": "blk_notes",
          "block_type": "note",
          "position": 1,
          "hidden": false,
          "block_group_id": null,
          "elements": [
            { "element_id": 54, "id": 54 },
            { "element_id": 674, "id": 674 },
            { "element_id": 524, "id": 524 }
          ]
        },
        {
          "id": 2,
          "title": "Additional notes",
          "slug": "blk_additional_notes",
          "block_type": "note",
          "position": 2,
          "hidden": false,
          "block_group_id": null,
          "elements": [
            { "element_id": 235, "id": 235 },
            { "element_id": 342, "id": 342 },
            { "element_id": 744, "id": 744 }
          ]
        }
      ],
      "tasks": [
        {
          "id": 1,
          "description": "Review security logs",
          "completed": false,
          "assignees": [
            {
              "id": "123"
            },
            {
              "id": "456"
            }
          ],
          "created_at": "2024-03-25T15:40:39Z",
          "updated_at": "2024-03-25T15:40:39Z"
        },
        {
          "id": 2,
          "description": "Contact affected users",
          "completed": false,
          "assignees": [],
          "created_at": "2024-03-25T15:40:39Z",
          "updated_at": "2024-03-25T15:40:39Z"
        }
      ],
      "activities_meta": {
        "current_page": "https://<tenant-domain>/api/v2/cases/1/activities?per_page=20&page=1",
        "previous_page": null,
        "next_page": null,
        "next_page_number": null,
        "per_page": 20,
        "pages": 1,
        "count": 3
      },
      "records_meta": {
        "current_page": "https://<tenant-domain>/api/v2/cases/1/records?per_page=20&page=1",
        "previous_page": null,
        "next_page": null,
        "next_page_number": null,
        "per_page": 20,
        "pages": 1,
        "count": 4
      }
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

<!-- cspell: enable -->

### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Delete a case.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <api-token>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Delete

## Description

Delete a case.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <api-token>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Append

## Description

Append to the case description.

## Request

HTTP Method: **POST**

| Parameter   | Description                                                                                                                                                           |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| description | The additional case description content. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/description \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"description": "...This is additional content to append to the existing description"}'
```

## Response

A successful request will return a JSON object describing the updated case.

### Field description

| Parameter          | Description                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| case_id            | The ID of the case.                                                                                                                   |
| name               | The case name.                                                                                                                        |
| description        | The case description.                                                                                                                 |
| status             | The case status - open or closed.                                                                                                     |
| sub_status         | An object describing the case sub-status.                                                                                             |
| url                | The case URL.                                                                                                                         |
| metadata           | Case related metadata represented as key-value pairs.                                                                                 |
| author             | An object describing the case author.                                                                                                 |
| opened_by          | An object describing the user who opened the case.                                                                                    |
| resolved_by        | An object describing the user who resolved the case.                                                                                  |
| assignees          | An array of users assigned to the case.                                                                                               |
| tags               | An array of tags associated with the case.                                                                                            |
| activities         | An array of team case actions taken within the case.                                                                                  |
| activities_meta    | Metadata defining pagination params required to fetch additional activities for the case.                                             |
| team               | The team the case is within - ID & name.                                                                                              |
| actions            | An array of case actions for downstream actions associated with the case.                                                             |
| linked_cases       | An array of cases linked to this case - ID & name.                                                                                    |
| sla                | An object describing the case SLA status. Times are in seconds.                                                                       |
| slas               | An array of SLAs for the case. Times are in seconds.                                                                                  |
| fields             | An array of field values for the case.                                                                                                |
| opened_at          | ISO 8601 Timestamp representing the date and time the case was opened at.                                                             |
| resolved_at        | ISO 8601 Timestamp representing the date and time the case was resolved at.                                                           |
| created_at         | ISO 8601 Timestamp representing the date and time the case was created at.                                                            |
| updated_at         | ISO 8601 Timestamp representing the date and time the case was updated at.                                                            |
| records            | An array of records associated with the case, grouped by record type.                                                                 |
| records_meta       | Metadata defining pagination params required to fetch additional records for the case.                                                |
| priority           | The case priority - critical, high, medium, low or info.                                                                              |
| closure_conditions | An array of closure requirements in the form of a set of formula rules that need to evaluate to true to enable the closing of a case. |
| blocks             | An array of block objects specifying the block type and block element ids.                                                            |
| tasks              | An array of tasks associated with the case.                                                                                           |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 1,
  "name": "Case 1..25",
  "description": "This is a high priority case created via the API...This is additional content to append to the description",
  "status": "OPEN",
  "sub_status": {
    "id": 1,
    "name": "To do"
  },
  "priority": "LOW",
  "url": "http://<tenant-domain>/team/276/cases/1",
  "metadata": {
    "foo": "bar"
  },
  "author": {
    "user_id": "168",
    "first_name": "Example",
    "last_name": "Person",
    "email": "person_2@example.com",
    "avatar_url": "",
    "is_service_account": false
  },
  "opened_by": null,
  "resolved_by": null,
  "assignees": [
    {
      "user_id": "176",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_10@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "178",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_12@example.com",
      "avatar_url": "",
      "is_service_account": false
    },
    {
      "user_id": "180",
      "first_name": "Example",
      "last_name": "Person",
      "email": "person_14@example.com",
      "avatar_url": "",
      "is_service_account": false
    }
  ],
  "tags": [
    {
      "id": 7,
      "name": "Tag 1"
    },
    {
      "id": 8,
      "name": "Tag 2"
    },
    {
      "id": 9,
      "name": "Tag 3"
    }
  ],
  "sla": {
    "remaining_time_seconds": 0,
    "current_time_seconds": 0,
    "percent_elapsed": 0,
    "exceeded": false,
    "sla_type": "completion"
  },
  "slas": [
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 0,
      "exceeded": false,
      "sla_type": "completion"
    },
    {
      "remaining_time_seconds": 0,
      "current_time_seconds": 0,
      "percent_elapsed": 100,
      "exceeded": true,
      "sla_type": "response"
    }
  ],
  "fields": [
    {
      "id": 1,
      "value": "Windows 11",
      "case_input": {
        "id": 1,
        "name": "Operating system"
      }
    }
  ],
  "activities": [
    {
      "id": 109,
      "activity_type": "CREATED",
      "value": "115",
      "user": {
        "user_id": "168",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_2@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 111,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "170",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_4@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 113,
      "activity_type": "COMMENTED",
      "value": "I'm a comment",
      "user": {
        "user_id": "172",
        "first_name": "Example",
        "last_name": "Person",
        "email": "person_6@example.com",
        "avatar_url": "",
        "is_service_account": false
      },
      "created_at": "2024-03-25T15:40:39Z"
    }
  ],
  "team": {
    "id": 276,
    "name": "Test team 1"
  },
  "linked_cases": [{ "case_id": 1, "name": "Case 1" }],
  "closure_conditions": [
    {
      "formula": "=SIZE(team_case.assignees) > 2",
      "name": "Number of assignees"
    }
  ],
  "opened_at": "2024-03-25T15:40:39Z",
  "resolved_at": null,
  "created_at": "2024-03-25T15:40:39Z",
  "updated_at": "2024-03-25T15:40:39Z",
  "records": [
    {
      "record_type_id": 55,
      "record_type_name": "Record type 2",
      "record_type_record_results": [
        {
          "id": 59,
          "results": [
            {
              "id": 59110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 59109,
              "name": "Story name",
              "value": "Alert investigation"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 58,
          "results": [
            {
              "id": 58110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 58109,
              "name": "Story name",
              "value": "Alert investigation #1"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    },
    {
      "record_type_id": 54,
      "record_type_name": "Record type 1",
      "record_type_record_results": [
        {
          "id": 56,
          "results": [
            {
              "id": 56108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 56107,
              "name": "Story name",
              "value": "Alert investigation #2"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 55,
          "results": [
            {
              "id": 55108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 55107,
              "name": "Story name",
              "value": "Alert investigation #3"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    }
  ],
  "actions": [
    {
      "id": 7,
      "url": "http://example.com",
      "label": "Hello, world!",
      "action_type": "page"
    }
  ],
  "blocks": [
    {
      "id": 1,
      "block_type": "note",
      "elements": [
        { "element_id": 54, "id": 54 },
        { "element_id": 674, "id": 674 },
        { "element_id": 524, "id": 524 }
      ]
    },
    {
      "id": 2,
      "block_type": "note",
      "elements": [
        { "element_id": 235, "id": 235 },
        { "element_id": 342, "id": 342 },
        { "element_id": 744, "id": 744 }
      ]
    }
  ],
  "tasks": [
    {
      "id": 1,
      "description": "Review security logs",
      "completed": false,
      "assignees": [
        {
          "id": "123"
        },
        {
          "id": "456"
        }
      ],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 2,
      "description": "Contact affected users",
      "completed": false,
      "assignees": [],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    }
  ],
  "activities_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/activities?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  },
  "records_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/records?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Actions

#### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Create a new case button on a specified case. This does not overwrite other buttons that may exist on the case.

## Request

HTTP Method: **POST**

| Parameter   | Description                                                 |
| ----------- | ----------------------------------------------------------- |
| url         | The URL of the case button (webhook or page URL).           |
| label       | User specified field to identify the case button.           |
| button_type | The case button type (`webhook` or `page`).                 |
| button_text | **Optional** The text for the button to execute the action. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/buttons \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "url": "https://example.tines.com",
        "label": "label",
        "button_type": "webhook",
        "button_text": "Open form"
      }'
```

## Response

A successful request will return a JSON object describing the newly created case button.

### Field description

| Parameter   | Description                                                                          |
| ----------- | ------------------------------------------------------------------------------------ |
| case_id     | The case ID.                                                                         |
| id          | The case button ID.                                                                  |
| url         | The URL of the case button.                                                          |
| label       | The label of the case button.                                                        |
| button_type | The case button type (`webhook` or `page`).                                          |
| button_text | The text for the button to execute the action.                                       |
| story_name  | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji | The associated story's emoji icon.                                                   |
| page_emoji  | The associated page's emoji icon.                                                    |
| created_at  | ISO 8601 Timestamp representing creation date and time of the case button.           |
| updated_at  | ISO 8601 Timestamp representing last updated date and time of case button.           |

### Sample response

```json
{
  "case_id": 42,
  "id": 1,
  "url": "https://tenant.tines.com/webhook/abc/",
  "label": "Claim case",
  "story_name": "Case Management",
  "story_emoji": ":fire:",
  "page_emoji": null,
  "button_type": "webhook",
  "button_text": "Claim",
  "created_at": "2023-10-31T15:42:00Z",
  "updated_at": "2023-10-31T16:42:00Z"
}
```

#### Create

## Description

Create a new case action on a specified case. This does not overwrite other actions that may exist on the case.

## Request

HTTP Method: **POST**

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| url          | The URL of the case action (webhook or page URL).                                          |
| label        | User specified field to identify the case action.                                          |
| action_type  | The case action type (`webhook` or `page`).                                                |
| action_text  | **Optional** The text for the button to execute the action.                                |
| query_params | **Optional** An object of key-value pairs to append as query parameters to the action URL. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/actions \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "url": "https://example.tines.com",
        "label": "label",
        "action_type": "webhook",
        "action_text": "Open form",
        "query_params": {
          "status": "open",
          "priority": "high"
        }
      }'
```

## Response

A successful request will return a JSON object describing the newly created case action.

### Field description

| Parameter    | Description                                                                          |
| ------------ | ------------------------------------------------------------------------------------ |
| case_id      | The ID of the case.                                                                  |
| id           | The case action ID.                                                                  |
| url          | The URL of the case action.                                                          |
| label        | The label of the case action.                                                        |
| action_type  | The case action type (`webhook` or `page`).                                          |
| action_text  | The text for the button to execute the action.                                       |
| story_name   | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji  | The associated story's emoji icon.                                                   |
| page_emoji   | The associated page's emoji icon.                                                    |
| query_params | An object of key-value pairs to append as query parameters to the action URL.        |
| created_at   | ISO 8601 Timestamp representing creation date and time of the case action.           |
| updated_at   | ISO 8601 Timestamp representing last updated date and time of case action.           |

### Sample response

```json
{
  "case_id": 42,
  "id": 1,
  "url": "https://tenant.tines.com/webhook/abc/",
  "label": "Claim case",
  "story_name": "Case Management",
  "story_emoji": ":fire:",
  "page_emoji": null,
  "action_type": "webhook",
  "action_text": "Open form",
  "query_params": {
    "priority": "high"
  },
  "created_at": "2023-10-31T15:42:00Z",
  "updated_at": "2023-10-31T16:42:00Z"
}
```

#### Get

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a specific case button.

## Request

HTTP Method: **GET**

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | ID of the case.        |
| id             | ID of the case button. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/buttons/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a case button.

### Field description

| Parameter   | Description                                                                          |
| ----------- | ------------------------------------------------------------------------------------ |
| case_id     | The case ID.                                                                         |
| id          | The case button ID.                                                                  |
| url         | The URL of the case button.                                                          |
| label       | The label of the case button.                                                        |
| button_type | The case button type (`webhook` or `page`).                                          |
| button_text | The text for the button to execute the action.                                       |
| story_name  | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji | The associated story's emoji icon.                                                   |
| page_emoji  | The associated page's emoji icon.                                                    |
| created_at  | ISO 8601 Timestamp representing creation date and time of the case button.           |
| updated_at  | ISO 8601 Timestamp representing last updated date and time of case button.           |

### Sample response

```json
{
  "case_id": 42,
  "id": 1,
  "url": "https://tenant.tines.com/webhook/abc/",
  "label": "Claim case",
  "story_name": "Case Management",
  "story_emoji": ":fire:",
  "page_emoji": null,
  "button_type": "webhook",
  "button_text": "Claim",
  "created_at": "2023-10-31T15:42:00Z",
  "updated_at": "2023-10-31T16:42:00Z"
}
```

#### Get

## Description

Retrieve a specific case action.

## Request

HTTP Method: **GET**

| Path Parameter | Description                |
| -------------- | -------------------------- |
| case_id        | The ID of the case.        |
| id             | The ID of the case action. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/actions/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a case button.

### Field description

| Parameter    | Description                                                                          |
| ------------ | ------------------------------------------------------------------------------------ |
| case_id      | The ID of the case.                                                                  |
| id           | The case button ID.                                                                  |
| url          | The URL of the case button.                                                          |
| label        | The label of the case button.                                                        |
| action_type  | The case action type (`webhook` or `page`).                                          |
| action_text  | The text for the button to execute the action.                                       |
| story_name   | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji  | The associated story's emoji icon.                                                   |
| page_emoji   | The associated page's emoji icon.                                                    |
| query_params | An object of key-value pairs to append as query parameters to the action URL.        |
| created_at   | ISO 8601 Timestamp representing creation date and time of the case button.           |
| updated_at   | ISO 8601 Timestamp representing last updated date and time of case button.           |

### Sample response

```json
{
  "case_id": 42,
  "id": 1,
  "url": "https://tenant.tines.com/webhook/abc/",
  "label": "Claim case",
  "story_name": "Case Management",
  "story_emoji": ":fire:",
  "page_emoji": null,
  "action_type": "webhook",
  "action_text": "Open form",
  "query_params": {
    "priority": "high"
  },
  "created_at": "2023-10-31T15:42:00Z",
  "updated_at": "2023-10-31T16:42:00Z"
}
```

#### Update

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Use a HTTP PUT request to update an existing case button.

## Request

HTTP Method: **PUT**

| Parameter   | Description                                                    |
| ----------- | -------------------------------------------------------------- |
| url         | **Optional** The URL of the case button (webhook or page URL). |
| label       | **Optional** User specified field to identify the case button. |
| button_type | **Optional** The case button type (`webhook` or `page`).       |
| button_text | **Optional** The text for the button to execute the action.    |

| Path Parameter | Description       |
| -------------- | ----------------- |
| case_id        | ID of the case.   |
| id             | ID of the button. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/buttons/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "url": "https://example.com"
      }'
```

## Response

A successful request will return a JSON object describing the updated case button.

### Field description

| Parameter   | Description                                                                          |
| ----------- | ------------------------------------------------------------------------------------ |
| case_id     | The case ID.                                                                         |
| id          | The case button ID.                                                                  |
| url         | The URL of the case button.                                                          |
| label       | The label of the case button.                                                        |
| button_type | The case button type (`webhook` or `page`).                                          |
| button_text | The text for the button to execute the action.                                       |
| story_name  | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji | The associated story's emoji icon.                                                   |
| page_emoji  | The associated page's emoji icon.                                                    |
| created_at  | ISO 8601 Timestamp representing creation date and time of the case button.           |
| updated_at  | ISO 8601 Timestamp representing last updated date and time of case button.           |

### Sample response

```json
{
  "case_id": 42,
  "id": 1,
  "url": "https://tenant.tines.com/webhook/abc/",
  "label": "Claim case",
  "story_name": "Case Management",
  "story_emoji": ":fire:",
  "page_emoji": null,
  "button_type": "webhook",
  "button_text": "Claim",
  "created_at": "2023-10-31T15:42:00Z",
  "updated_at": "2023-10-31T16:42:00Z"
}
```

#### Update

## Description

Use a HTTP PUT request to update an existing case action.

## Request

HTTP Method: **PUT**

| Parameter    | Description                                                                                |
| ------------ | ------------------------------------------------------------------------------------------ |
| url          | **Optional** The URL of the case action (webhook or page URL).                             |
| label        | **Optional** User specified field to identify the case action.                             |
| action_type  | **Optional** The case action type (`webhook` or `page`).                                   |
| action_text  | **Optional** The text for the button to execute the action.                                |
| query_params | **Optional** An object of key-value pairs to append as query parameters to the action URL. |

| Path Parameter | Description           |
| -------------- | --------------------- |
| case_id        | The ID of the case.   |
| id             | The ID of the action. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/actions/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "url": "https://example.com"
      }'
```

## Response

A successful request will return a JSON object describing the updated case action.

### Field description

| Parameter    | Description                                                                          |
| ------------ | ------------------------------------------------------------------------------------ |
| case_id      | The ID of the case.                                                                  |
| id           | The case action ID.                                                                  |
| url          | The URL of the case action.                                                          |
| label        | The label of the case action.                                                        |
| action_type  | The case action type (`webhook` or `page`).                                          |
| action_text  | The text for the button to execute the action.                                       |
| query_params | An object of key-value pairs to append as query parameters to the action URL.        |
| story_name   | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji  | The associated story's emoji icon.                                                   |
| page_emoji   | The associated page's emoji icon.                                                    |
| created_at   | ISO 8601 Timestamp representing creation date and time of the case action.           |
| updated_at   | ISO 8601 Timestamp representing last updated date and time of case action.           |

### Sample response

```json
{
  "case_id": 42,
  "id": 1,
  "url": "https://tenant.tines.com/webhook/abc/",
  "label": "Claim case",
  "story_name": "Case Management",
  "story_emoji": ":fire:",
  "page_emoji": null,
  "action_type": "webhook",
  "action_text": "Open form",
  "query_params": {
    "priority": "high"
  },
  "created_at": "2023-10-31T15:42:00Z",
  "updated_at": "2023-10-31T16:42:00Z"
}
```

#### List

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a list of case buttons for a specific case.

## Request

HTTP Method: **GET**

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/buttons \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array containing the case buttons on the specified case provided the requesting token has access to it.

### Field description

| Parameter   | Description                                                                          |
| ----------- | ------------------------------------------------------------------------------------ |
| case_id     | The case ID.                                                                         |
| id          | The case button ID.                                                                  |
| url         | The URL of the case button.                                                          |
| label       | The label of the case button.                                                        |
| button_type | The case button type (`webhook` or `page`).                                          |
| story_name  | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji | The associated story's emoji icon.                                                   |
| page_emoji  | The associated page's emoji icon.                                                    |
| created_at  | ISO 8601 Timestamp representing creation date and time of the case button.           |
| updated_at  | ISO 8601 Timestamp representing last updated date and time of case button.           |

### Sample response

```json
"case_id": 1,
"team_case_buttons": [
  {
    "id": 1,
    "url": "https://tenant.tines.com/webhook/abc/",
    "label": "Claim case",
    "story_name": "Case Management",
    "story_emoji": ":fire:",
    "page_emoji": null,
    "button_type": "webhook",
    "created_at": "2023-10-31T15:42:00Z",
    "updated_at": "2023-10-31T16:42:00Z"
  },
  {
    "id": 2,
    "url": "https://tenant.tines.com/webhook/abc/",
    "label": "Escalate",
    "story_name": "Case Management",
    "story_emoji": ":rocket:",
    "page_emoji": null,
    "button_type": "webhook",
    "created_at": "2023-10-31T17:42:00Z",
    "updated_at": "2023-10-31T18:42:00Z"
  }
]
```

#### List

## Description

Retrieve a list of case actions for a specific case.

## Request

HTTP Method: **GET**

| Path Parameter | Description                                                                                         |
| -------------- | --------------------------------------------------------------------------------------------------- |
| case_id        | The ID of the case.                                                                                 |
| per_page       | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page           | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

### Sample request

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/actions \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array containing the case actions on the specified case provided the requesting token has access to it.

### Field description

| Parameter    | Description                                                                          |
| ------------ | ------------------------------------------------------------------------------------ |
| case_id      | The ID of the case.                                                                  |
| id           | The case action ID.                                                                  |
| url          | The URL of the case action.                                                          |
| label        | The label of the case action.                                                        |
| action_type  | The case action type (`webhook` or `page`).                                          |
| action_text  | The text for the button to execute the action.                                       |
| story_name   | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji  | The associated story's emoji icon.                                                   |
| page_emoji   | The associated page's emoji icon.                                                    |
| query_params | An object of key-value pairs to append as query parameters to the action URL.        |
| created_at   | ISO 8601 Timestamp representing creation date and time of the case action.           |
| updated_at   | ISO 8601 Timestamp representing last updated date and time of case action.           |

### Sample response

```json
{
  "case_id": 1,
  "actions": [
    {
      "id": 1,
      "url": "https://tenant.tines.com/webhook/abc/",
      "label": "Claim case",
      "story_name": "Case Management",
      "story_emoji": ":fire:",
      "page_emoji": null,
      "action_type": "webhook",
      "action_text": "Open form",
      "query_params": {
        "assignee": "john@doe.com"
      },
      "created_at": "2023-10-31T15:42:00Z",
      "updated_at": "2023-10-31T16:42:00Z"
    },
    {
      "id": 2,
      "url": "https://tenant.tines.com/webhook/abc/",
      "label": "Escalate",
      "story_name": "Case Management",
      "story_emoji": ":rocket:",
      "page_emoji": null,
      "action_type": "webhook",
      "action_text": "Open page",
      "query_params": {
        "status": "open",
        "priority": "high"
      },
      "created_at": "2023-10-31T17:42:00Z",
      "updated_at": "2023-10-31T18:42:00Z"
    }
  ],
  "meta": {
    "count": 2,
    "current_page": "https://<tenant-domain>/api/v2/cases/1/actions?per_page=20&page=1",
    "next_page": null,
    "next_page_number": null,
    "pages": 1,
    "per_page": 20,
    "previous_page": null
  }
}
```

#### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Delete an existing case button.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description                      |
| -------------- | -------------------------------- |
| case_id        | ID of the case.                  |
| id             | ID of the case button to delete. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/buttons/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete

## Description

Delete an existing case action.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description                      |
| -------------- | -------------------------------- |
| case_id        | The ID of the case.              |
| id             | ID of the case action to delete. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/actions/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Batch update

## Description

Update the actions on a case. This request will replace any existing actions on the case.

## Request

HTTP Method: **PUT**

| Actions Parameter | Description                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------ |
| url               | The URL of the case action (webhook or page URL).                                          |
| label             | User specified field to identify the case action.                                          |
| action_type       | The case action type (`webhook` or `page`).                                                |
| action_text       | **Optional** The text for the button to execute the action.                                |
| query_params      | **Optional** An object of key-value pairs to append as query parameters to the action URL. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/actions/batch \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "actions": [
          {
            "url": "https://tenant.tines.com/pages/123",
            "label": "Complete request",
            "action_type": "page",
            "action_text": "Open",
            "query_params": {
              "status": "done"
            }
          },
          {
            "url": "https://tenant.tines.com/webhook/abc",
            "label": "Claim case",
            "action_type": "webhook",
            "action_text": "Run",
            "query_params": {
              "status": "open"
            }
          }
        ]
      }'
```

## Response

A successful request will return a JSON object describing the updated case action.

### Field description

| Parameter | Description                        |
| --------- | ---------------------------------- |
| case_id   | The ID of the case.                |
| actions   | An array of the actions on a case. |

| Action object | Description                                                                          |
| ------------- | ------------------------------------------------------------------------------------ |
| id            | The case action ID.                                                                  |
| url           | The URL of the case action.                                                          |
| label         | The label of the case action.                                                        |
| action_type   | The case action type (`webhook` or `page`).                                          |
| action_text   | The text for the button to execute the action.                                       |
| story_name    | Will be populated if the URL matches that of a page or webhook in an existing story. |
| story_emoji   | The associated story's emoji icon.                                                   |
| page_emoji    | The associated page's emoji icon.                                                    |
| created_at    | ISO 8601 Timestamp representing creation date and time of the case action.           |
| updated_at    | ISO 8601 Timestamp representing last updated date and time of case action.           |

### Sample response

```json
{
  "case_id": 42,
  "actions": [
    {
      "id": 1,
      "url": "https://tenant.tines.com/webhook/abc",
      "label": "Claim case",
      "story_name": "Case Management",
      "story_emoji": ":fire:",
      "page_emoji": null,
      "action_type": "webhook",
      "action_text": "Run",
      "query_params": {
        "assignee": "john@doe.com"
      },
      "created_at": "2023-10-31T15:42:00Z",
      "updated_at": "2023-10-31T16:42:00Z"
    },
    {
      "id": 1,
      "url": "https://tenant.tines.com/pages/123",
      "label": "Complete request",
      "story_name": "Case Management",
      "story_emoji": ":fire:",
      "page_emoji": ":fire:",
      "action_type": "page",
      "action_text": "Open page",
      "query_params": {
        "status": "open",
        "priority": "high"
      },
      "created_at": "2023-10-31T15:42:00Z",
      "updated_at": "2023-10-31T16:42:00Z"
    }
  ]
}
```

### Activities

#### Get

## Description

Retrieve a single case activity.

## Request

HTTP Method: **GET**

| Query Parameter | Description                  |
| --------------- | ---------------------------- |
| case_id         | The ID of the case.          |
| activity_id     | The ID of the case activity. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/activities/<<activity_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the case activity.

### Field description

| Parameter     | Description                                                                                                                                  |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| case_id       | The ID of the case.                                                                                                                          |
| id            | The activity ID.                                                                                                                             |
| activity_type | The type of activity.                                                                                                                        |
| value         | The value of the activity.                                                                                                                   |
| file          | An object describing the file attached to the activity. Available only for `FILE_ATTACHED` or `FILE_ATTACHED_AND_COMMENTED` type activities. |
| user          | An object describing the user who performed the activity.                                                                                    |
| created_at    | The timestamp of the activity.                                                                                                               |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 10,
  "id": 10,
  "activity_type": "FILE_ATTACHED_AND_COMMENTED",
  "value": "This is a case comment",
  "file": {
    "filename": "foo",
    "url": "http://<tenant-domain>/api/v2/cases/10/files/10/download"
  },
  "user": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "created_at": "2023-07-18T10:27:18Z"
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of case activities for a case. These activities are logged on the case timeline and can be filtered by type.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| case_id         | Retrieve activities for the specified case.                                                         |
| activity_type   | **Optional** An array of types of activities requested. See the list below for supported types.     |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 50.            |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

Supported activity types

- `CREATED`
- `ASSIGNED`
- `UNASSIGNED`
- `STATUS_UPDATED`
- `SUB_STATUS_UPDATED`
- `SEVERITY_UPDATED`
- `COMMENTED`
- `DELETED_COMMENT`
- `DESCRIPTION_UPDATED`
- `FIELD_UPDATED`
- `FILE_ATTACHED`
- `FILE_ATTACHED_AND_COMMENTED`
- `FILE_DELETED`
- `TAGS_ADDED`
- `TAGS_REMOVED`
- `RECORD_RESULT_SET_ADDED`
- `RECORD_RESULT_SET_REMOVED`
- `CHECKLIST_ITEM_COMPLETED`
- `CHECKLIST_ITEM_INCOMPLETE`
- `LINKED_CASE_ADDED`
- `LINKED_CASE_REMOVED`
- `METADATA_UPDATED`
- `NOTE_ADDED`
- `NOTE_DELETED`
- `NOTE_UPDATED`
- `SLA_WARNING`
- `SLA_EXCEEDED`
- `TASK_CREATED`
- `TASK_DELETED`
- `TASK_COMPLETED`
- `TASK_INCOMPLETE`
- `TASK_UPDATED`
- `TASK_ASSIGNED`
- `TASK_UNASSIGNED`
- `TEAM_UPDATED`
- `TITLE_UPDATED`

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/activities?activity_type=%5B%5D=<<type>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the cases the requesting token has access to.

### Field description

| Parameter     | Description                                                                                                                                  |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| id            | The activity ID.                                                                                                                             |
| activity_type | The type of activity.                                                                                                                        |
| value         | The value of the activity.                                                                                                                   |
| file          | An object describing the file attached to the activity. Available only for `FILE_ATTACHED` or `FILE_ATTACHED_AND_COMMENTED` type activities. |
| user          | An object describing the user who performed the activity.                                                                                    |
| created_at    | The timestamp of the activity.                                                                                                               |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 10,
  "activities": [
    {
      "id": 10,
      "activity_type": "FILE_ATTACHED_AND_COMMENTED",
      "value": "This is a case comment",
      "file": {
        "filename": "foo",
        "url": "http://<tenant-domain>/api/v2/cases/10/files/10/download"
      },
      "user": {
        "id": 1,
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@tines.io",
        "avatar_url": "example.com/avatar",
        "is_service_account": false
      },
      "created_at": "2023-07-18T10:27:18Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/<<case_id>>/activities?activity_type=%5B%5D=<<type>>?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

<!-- cspell:enable -->

### Assignees

#### List

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a list of assignees of a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/assignees \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the assignees.

### Field description

| Parameter | Description                             |
| --------- | --------------------------------------- |
| case_id   | The case ID.                            |
| assignees | An array of users assigned to the case. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "assignees": [
    {
      "user_id": "1",
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "avatar_url": "example.com/avatar",
      "is_service_account": false
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/cases/42/assignees?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### List

## Description

Retrieve a list of assignees of a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/assignees \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the assignees.

### Field description

| Parameter | Description                             |
| --------- | --------------------------------------- |
| case_id   | The case ID.                            |
| assignees | An array of users assigned to the case. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "assignees": [
    {
      "user_id": "1",
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "avatar_url": "example.com/avatar",
      "is_service_account": false
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/42/assignees?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

### Blocks

#### Create

## Description

Add a block to a case.

## Request

HTTP Method: **POST**

| Parameter      | Description                                                                                                                       |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| title          | The title of the block.                                                                                                           |
| block_type     | The type of block to create - options: note, file, linked_cases, metadata, closure_conditions, case_action, block_group and html. |
| elements       | **Optional** An array of elements to add to the block.                                                                            |
| position       | **Optional** The zero-indexed position of the block in the case.                                                                  |
| hidden         | **Optional** A boolean (true or false).                                                                                           |
| block_group_id | **Optional** The ID or slug of a group block to nest this block within. Slugs are prefixed with `blk_` (e.g. `blk_my_group`).     |
| author_email   | **Optional** The email address of the author of the block.                                                                        |

### Element Parameters

#### Note Element Parameters

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                                            |
| note_type | The type of note - "html" for html blocks and "text" for any others.                                |
| color     | **Optional** The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

#### File Element Parameters

| Parameter     | Description                                    |
| ------------- | ---------------------------------------------- |
| filename      | The name of the file.                          |
| file_contents | The base64-encoded contents of the file.       |
| annotation    | **Optional** The annotation text for the file. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request for a note block

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "title": "My Note Block",
        "block_type": "note",
        "author_email": "jane@tines.io",
        "elements": [
          {
            "content": "This is a note element",
            "note_type": "text",
            "color": "white"
          }
        ],
        "position": 0
      }'
```

### Sample request for a file block

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "title": "My File Block",
        "block_type": "file",
        "elements": [
          {
            "filename": "example.txt",
            "file_contents": "SGVsbG8gV29ybGQ="
          }
        ],
        "position": 1
      }'
```

## Response

A successful request will return a JSON object describing the created block.

### Field description

| Parameter | Description       |
| --------- | ----------------- |
| block     | The block object. |

| Block Parameter | Description                                                                                                                 |
| --------------- | --------------------------------------------------------------------------------------------------------------------------- |
| id              | The ID of the block record attached to the case.                                                                            |
| slug            | A human-readable identifier for the block, prefixed with `blk_`. Auto-generated from the block title.                       |
| title           | The title of the block.                                                                                                     |
| block_type      | The type of the block - options: note, file, linked_cases, metadata, closure_conditions, case_action, block_group and html. |
| position        | The position of the block in the case.                                                                                      |
| hidden          | Boolean indicating whether the block is hidden.                                                                             |
| block_group_id  | The ID of the block group this block is nested in.                                                                          |
| elements        | An array of elements contained within the block.                                                                            |
| created_at      | ISO 8601 Timestamp representing creation date and time.                                                                     |
| updated_at      | ISO 8601 Timestamp representing last updated date and time.                                                                 |

#### Common Element Parameters

| Parameter    | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| element_id   | The unique ID of this element within the current block.     |
| id           | The original/source ID of the element.                      |
| element_type | The type of the element - options: note, file.              |
| author       | Details of the author of the element.                       |
| created_at   | ISO 8601 Timestamp representing creation date and time.     |
| updated_at   | ISO 8601 Timestamp representing last updated date and time. |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

#### Note Element Parameters

| Parameter | Description                                                                            |
| --------- | -------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                               |
| note_type | The type of note - "html" for html blocks and "text" for any others.                   |
| color     | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

#### File Element Parameters

| Parameter | Description                                            |
| --------- | ------------------------------------------------------ |
| file      | Object containing file information (filename and url). |

### Sample response for a note block

<!-- cspell:disable -->

```json
{
  "block": {
    "id": 123,
    "slug": "blk_my_note_block",
    "title": "My Note Block",
    "block_type": "note",
    "position": 0,
    "hidden": false,
    "block_group_id": null,
    "elements": [
      {
        "element_id": 456,
        "id": 123,
        "element_type": "note",
        "content": "This is a note element",
        "note_type": "text",
        "color": "white",
        "author": {
          "user_id": "1",
          "first_name": "Jane",
          "last_name": "Doe",
          "email": "jane@tines.io",
          "avatar_url": "example.com/avatar",
          "is_service_account": false
        },
        "created_at": "2025-01-07T11:42:58Z",
        "updated_at": "2025-01-07T11:42:58Z"
      }
    ],
    "created_at": "2025-01-07T11:42:58Z",
    "updated_at": "2025-01-07T11:42:58Z"
  }
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a single block for a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                             |
| --------- | --------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                     |
| block_id  | The ID or slug of the block. Slugs are prefixed with `blk_` (e.g. `blk_my_note_block`). |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks/<<block_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

> You can use a block's slug in place of a numeric ID in this endpoint. For example, `/api/v2/cases/1/blocks/blk_note_block`.

## Response

A successful request will return a JSON object of the block.

### Field description

| Parameter | Description       |
| --------- | ----------------- |
| block     | The block object. |

| Block Parameter | Description                                                                                                                 |
| --------------- | --------------------------------------------------------------------------------------------------------------------------- |
| id              | The ID of the block record attached to the case.                                                                            |
| slug            | A human-readable identifier for the block, prefixed with `blk_`. Auto-generated from the block title.                       |
| title           | The title of the block.                                                                                                     |
| block_type      | The type of the block - options: note, file, linked_cases, metadata, closure_conditions, case_action, block_group and html. |
| position        | The position of the block in the case.                                                                                      |
| hidden          | Boolean indicating whether the block is hidden.                                                                             |
| block_group_id  | The ID of the block group this block is nested in.                                                                          |
| elements        | An array of elements contained within the block.                                                                            |
| created_at      | ISO 8601 Timestamp representing creation date and time.                                                                     |
| updated_at      | ISO 8601 Timestamp representing last updated date and time.                                                                 |

#### Common Element Parameters

| Parameter    | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| element_id   | The unique ID of this element within the current block.     |
| id           | The original/source ID of the element.                      |
| element_type | The type of the element - matches the block_type.           |
| author       | Details of the author of the element.                       |
| created_at   | ISO 8601 Timestamp representing creation date and time.     |
| updated_at   | ISO 8601 Timestamp representing last updated date and time. |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

#### Note Element Parameters

| Parameter | Description                                                                             |
| --------- | --------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                                |
| note_type | The type of note - The type of note - "html" for html blocks and "text" for any others. |
| color     | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo.  |

#### File Element Parameters

| Parameter | Description                                            |
| --------- | ------------------------------------------------------ |
| file      | Object containing file information (filename and url). |

### Sample response

<!-- cspell:disable -->

```json
{
  "block": {
    "id": 123,
    "slug": "blk_note_block",
    "title": "Note Block",
    "block_type": "note",
    "position": 1,
    "hidden": false,
    "block_group_id": 456,
    "elements": [
      {
        "element_id": 456,
        "id": 123,
        "element_type": "note",
        "content": "This is a note element",
        "note_type": "text",
        "color": "white",
        "author": {
          "user_id": "1",
          "first_name": "Jane",
          "last_name": "Doe",
          "email": "jane@tines.io",
          "avatar_url": "example.com/avatar",
          "is_service_account": false
        },
        "created_at": "2025-01-07T11:42:58Z",
        "updated_at": "2025-01-07T11:42:58Z"
      }
    ],
    "created_at": "2025-01-07T11:42:58Z",
    "updated_at": "2025-01-07T11:42:58Z"
  }
}
```

<!-- cspell:enable -->

#### Update

## Description

Update an existing block.

## Request

HTTP Method: **PUT**

| Parameter      | Description                                                                                                                                                              |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| title          | **Optional** The title of the block.                                                                                                                                     |
| position       | **Optional** The zero-indexed position of the block in the case.                                                                                                         |
| hidden         | **Optional** A boolean (true or false).                                                                                                                                  |
| block_group_id | **Optional** The ID or slug of a block group to nest this block within, or `null` to remove it from a block group. Slugs are prefixed with `blk_` (e.g. `blk_my_group`). |

| Path Parameter | Description                                                                             |
| -------------- | --------------------------------------------------------------------------------------- |
| case_id        | The ID of the case.                                                                     |
| block_id       | The ID or slug of the block. Slugs are prefixed with `blk_` (e.g. `blk_my_note_block`). |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks/<<block_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "title": "Updated Block Title",
        "position": 2
      }'
```

## Response

A successful request will return a JSON object describing the updated block.

### Field description

| Parameter | Description       |
| --------- | ----------------- |
| block     | The block object. |

| Block Parameter | Description                                                                                                                                        |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| id              | The ID of the block record attached to the case.                                                                                                   |
| slug            | A human-readable identifier for the block, prefixed with `blk_`. Auto-generated from the block title. Updating the title will regenerate the slug. |
| title           | The title of the block.                                                                                                                            |
| block_type      | The type of the block - options: note, file, linked_cases, metadata, closure_conditions, case_action, block_group and html.                        |
| position        | The position of the block in the case.                                                                                                             |
| hidden          | Boolean indicating whether the block is hidden.                                                                                                    |
| block_group_id  | The ID of the block group this block is nested in.                                                                                                 |
| elements        | An array of elements contained within the block.                                                                                                   |
| created_at      | ISO 8601 Timestamp representing creation date and time.                                                                                            |
| updated_at      | ISO 8601 Timestamp representing last updated date and time.                                                                                        |

#### Common Element Parameters

| Parameter    | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| element_id   | The unique ID of this element within the current block.     |
| id           | The original/source ID of the element.                      |
| element_type | The type of the element - options: note, file.              |
| author       | Details of the author of the element.                       |
| created_at   | ISO 8601 Timestamp representing creation date and time.     |
| updated_at   | ISO 8601 Timestamp representing last updated date and time. |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

#### Note Element Parameters

| Parameter | Description                                                                            |
| --------- | -------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                               |
| note_type | The type of note - "html" for html blocks and "text" for any others.                   |
| color     | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

#### File Element Parameters

| Parameter | Description                                            |
| --------- | ------------------------------------------------------ |
| file      | Object containing file information (filename and url). |

### Sample response

<!-- cspell:disable -->

```json
{
  "block": {
    "id": 123,
    "slug": "blk_updated_block_title",
    "title": "Updated Block Title",
    "block_type": "note",
    "position": 2,
    "hidden": false,
    "block_group_id": null,
    "elements": [
      {
        "element_id": 456,
        "id": 123,
        "element_type": "note",
        "content": "This is a note element",
        "note_type": "text",
        "color": "white",
        "author": {
          "user_id": "1",
          "first_name": "Jane",
          "last_name": "Doe",
          "email": "jane@tines.io",
          "avatar_url": "example.com/avatar",
          "is_service_account": false
        },
        "created_at": "2025-01-07T11:42:58Z",
        "updated_at": "2025-01-07T11:42:58Z"
      }
    ],
    "created_at": "2025-01-07T11:42:58Z",
    "updated_at": "2025-01-08T10:15:30Z"
  }
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of blocks for a case.

## Request

HTTP Method: **GET**

| Parameter  | Description                                                                                                                              |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| case_id    | The ID of the case.                                                                                                                      |
| block_type | **Optional** Filter blocks by type - options: note, file, linked_cases, metadata, closure_conditions, case_action, block_group and html. |
| per_page   | **Optional** Set the number of results returned per page. Defaults to 20.                                                                |
| page       | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                                      |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the blocks.

### Field description

| Parameter | Description                                   |
| --------- | --------------------------------------------- |
| blocks    | An array of block objects attached to a case. |

| Block Parameter | Description                                                                                                                 |
| --------------- | --------------------------------------------------------------------------------------------------------------------------- |
| id              | The ID of the block record attached to the case.                                                                            |
| slug            | A human-readable identifier for the block, prefixed with `blk_`. Auto-generated from the block title.                       |
| title           | The title of the block.                                                                                                     |
| block_type      | The type of the block - options: note, file, linked_cases, metadata, closure_conditions, case_action, block_group and html. |
| position        | The position of the block in the case.                                                                                      |
| hidden          | Boolean indicating whether the block is hidden.                                                                             |
| block_group_id  | The ID of the block group this block is nested in.                                                                          |
| elements        | An array of elements contained within the block.                                                                            |
| created_at      | ISO 8601 Timestamp representing creation date and time.                                                                     |
| updated_at      | ISO 8601 Timestamp representing last updated date and time.                                                                 |

#### Common Element Parameters

| Parameter    | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| element_id   | The unique ID of this element within the current block.     |
| id           | The original/source ID of the element.                      |
| element_type | The type of the element - options: note, file.              |
| author       | Details of the author of the element.                       |
| created_at   | ISO 8601 Timestamp representing creation date and time.     |
| updated_at   | ISO 8601 Timestamp representing last updated date and time. |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

#### Note Element Parameters

| Parameter | Description                                                                            |
| --------- | -------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                               |
| note_type | The type of note - "html" for html blocks and "text" for any others.                   |
| color     | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

#### File Element Parameters

| Parameter | Description                                            |
| --------- | ------------------------------------------------------ |
| file      | Object containing file information (filename and url). |

### Sample response

<!-- cspell:disable -->

```json
{
  "blocks": [
    {
      "id": 123,
      "slug": "blk_note_block",
      "title": "Note Block",
      "block_type": "note",
      "position": 0,
      "hidden": false,
      "block_group_id": null,
      "elements": [
        {
          "element_id": 456,
          "id": 123,
          "element_type": "note",
          "content": "This is a note element",
          "note_type": "text",
          "color": "white",
          "author": {
            "user_id": "1",
            "first_name": "Jane",
            "last_name": "Doe",
            "email": "jane@tines.io",
            "avatar_url": "example.com/avatar",
            "is_service_account": false
          },
          "created_at": "2025-01-07T11:42:58Z",
          "updated_at": "2025-01-07T11:42:58Z"
        }
      ],
      "created_at": "2025-01-07T11:42:58Z",
      "updated_at": "2025-01-07T11:42:58Z"
    },
    {
      "id": 124,
      "slug": "blk_file_block",
      "title": "File Block",
      "block_type": "file",
      "position": 1,
      "hidden": false,
      "elements": [
        {
          "id": 457,
          "element_type": "file",
          "file": {
            "filename": "example.txt",
            "url": "https://example.com/files/example.txt"
          },
          "author": {
            "user_id": "1",
            "first_name": "Jane",
            "last_name": "Doe",
            "email": "jane@tines.io",
            "avatar_url": "example.com/avatar",
            "is_service_account": false
          },
          "created_at": "2025-01-07T11:43:58Z",
          "updated_at": "2025-01-07T11:43:58Z"
        }
      ],
      "created_at": "2025-01-07T11:43:58Z",
      "updated_at": "2025-01-07T11:43:58Z"
    }
  ]
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a block from a case.

## Request

HTTP Method: **DELETE**

| Path Parameter   | Description                                                                                        |
| ---------------- | -------------------------------------------------------------------------------------------------- |
| case_id          | The ID of the case.                                                                                |
| block_id         | The ID or slug of the block. Slugs are prefixed with `blk_` (e.g. `blk_my_note_block`).            |
| include_children | (For group blocks only) Boolean flag indicating whether the block's child blocks should be deleted |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks/<<block_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Elements

##### Get

## Description

Retrieve a single element from a block.

## Request

HTTP Method: **GET**

| Parameter  | Description            |
| ---------- | ---------------------- |
| case_id    | The ID of the case.    |
| block_id   | The ID of the block.   |
| element_id | The ID of the element. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks/<<block_id>>/elements/<<element_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object of the element.

### Field description

#### Common Element Parameters

| Parameter    | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| element_id   | The unique ID of this element within the current block.     |
| id           | The original/source ID of the element.                      |
| element_type | The type of the element - options: note, file.              |
| author       | Details of the author of the element.                       |
| created_at   | ISO 8601 Timestamp representing creation date and time.     |
| updated_at   | ISO 8601 Timestamp representing last updated date and time. |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

#### Note Element Parameters

| Parameter | Description                                                                            |
| --------- | -------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                               |
| note_type | The type of note - "html" for html blocks and "text" for any others.                   |
| color     | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

#### File Element Parameters

| Parameter | Description                                            |
| --------- | ------------------------------------------------------ |
| file      | Object containing file information (filename and url). |

### Sample response for a note element

<!-- cspell:disable -->

```json
{
  "element_id": 456,
  "id": 123,
  "element_type": "note",
  "content": "This is a note element",
  "note_type": "text",
  "color": "white",
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "created_at": "2025-01-07T11:42:58Z",
  "updated_at": "2025-01-07T11:42:58Z"
}
```

### Sample response for a file element

```json
{
  "id": 457,
  "element_type": "file",
  "file": {
    "filename": "example.txt",
    "url": "https://example.com/files/example.txt"
  },
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "created_at": "2025-01-07T11:43:58Z",
  "updated_at": "2025-01-07T11:43:58Z"
}
```

<!-- cspell:enable -->

##### Update

## Description

Update an existing element in a block. Currently, note elements and file elements can be updated.

## Request

HTTP Method: **PUT**

### Note Element Parameters

| Parameter    | Description                                                                                         |
| ------------ | --------------------------------------------------------------------------------------------------- |
| content      | **Optional** The content of the note.                                                               |
| color        | **Optional** The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| author_email | **Optional** The email address of the author of the note.                                           |

### File Element Parameters

| Parameter  | Description                                    |
| ---------- | ---------------------------------------------- |
| annotation | **Optional** The annotation text for the file. |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | The ID of the case.    |
| block_id       | The ID of the block.   |
| element_id     | The ID of the element. |

### Sample request for updating a note element

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks/<<block_id>>/elements/<<element_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "content": "This is an updated note element",
        "color": "blue",
        "author_email": "jane@tines.io"
      }'
```

### Sample request for updating a file annotation

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks/<<block_id>>/elements/<<element_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "annotation": "This is an annotation for the attached file"
      }'
```

## Response

A successful request will return a JSON object describing the updated element.

### Field description

#### Common Element Parameters

| Parameter    | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| element_id   | The unique ID of this element within the current block.     |
| id           | The original/source ID of the element.                      |
| element_type | The type of the element - options: note, file.              |
| author       | Details of the author of the element.                       |
| created_at   | ISO 8601 Timestamp representing creation date and time.     |
| updated_at   | ISO 8601 Timestamp representing last updated date and time. |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

#### Note Element Parameters

| Parameter | Description                                                                            |
| --------- | -------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                               |
| note_type | The type of note - "html" for html blocks and "text" for any others.                   |
| color     | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

#### File Element Parameters

| Parameter  | Description                                                      |
| ---------- | ---------------------------------------------------------------- |
| file       | Object containing file information.                              |
| annotation | The annotation text associated with the file, if one is present. |

| File parameter | Description                   |
| -------------- | ----------------------------- |
| filename       | The name of the file.         |
| url            | The URL to download the file. |

### Sample response for a note element

<!-- cspell:disable -->

```json
{
  "element_id": 456,
  "id": 123,
  "element_type": "note",
  "content": "This is an updated note element",
  "note_type": "text",
  "color": "blue",
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "created_at": "2025-01-07T11:42:58Z",
  "updated_at": "2025-01-08T10:15:30Z"
}
```

### Sample response for a file element with annotation

```json
{
  "element_id": 789,
  "id": 456,
  "element_type": "file",
  "file": {
    "filename": "report.pdf",
    "url": "https://example.com/api/v2/cases/123/files/456/download"
  },
  "annotation": "This is an annotation for the attached file",
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "created_at": "2025-01-07T11:42:58Z",
  "updated_at": "2025-01-08T10:15:30Z"
}
```

<!-- cspell:enable -->

##### Append

## Description

Append to the element content. Only note elements can be appended to.

## Request

HTTP Method: **POST**

### Note Element Parameters

| Parameter    | Description                                               |
| ------------ | --------------------------------------------------------- |
| content      | The additional content of the element.                    |
| author_email | **Optional** The email address of the author of the note. |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | The ID of the case.    |
| block_id       | The ID of the block.   |
| element_id     | The ID of the element. |

### Sample request for appending to a note element

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/blocks/<<block_id>>/elements/<<element_id>>/content \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"content": "...This is additional content to append to the existing content."}'
```

## Response

A successful request will return a JSON object describing the updated element.

### Field description

#### Common Element Parameters

| Parameter    | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| element_id   | The unique ID of this element within the current block.     |
| id           | The original/source ID of the element.                      |
| element_type | The type of the element - options: note, file.              |
| author       | Details of the author of the element.                       |
| created_at   | ISO 8601 Timestamp representing creation date and time.     |
| updated_at   | ISO 8601 Timestamp representing last updated date and time. |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

#### Note Element Parameters

| Parameter | Description                                                                            |
| --------- | -------------------------------------------------------------------------------------- |
| content   | The content of the note.                                                               |
| note_type | The type of note - "html" for html blocks and "text" for any others.                   |
| color     | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |

### Sample response for a note element

<!-- cspell:disable -->

```json
{
  "element_id": 456,
  "id": 123,
  "element_type": "note",
  "content": "This is a note element...This is additional content to append to the existing content.",
  "note_type": "text",
  "color": "blue",
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "created_at": "2025-01-07T11:42:58Z",
  "updated_at": "2025-01-08T10:15:30Z"
}
```

<!-- cspell:enable -->

### Bulk

#### Fields

##### Bulk update

## Description

Update fields on multiple cases at once.

## Request

HTTP Method: **POST**

| Parameter | Description                         |
| --------- | ----------------------------------- |
| case_ids  | An array of case IDs to update.     |
| fields    | An array of field objects to apply. |

| Field Parameter | Description                                                                                                                                   |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| input_id        | The ID of the case input to use with the field.                                                                                               |
| value           | The value of the field.                                                                                                                       |
| upsert          | **Optional** By default, only cases already associated with this field are updated. Set to true to add the field to cases that don't have it. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/bulk/fields \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "case_ids": [42, 43, 44],
        "fields": [
          {
            "input_id": 1,
            "value": "High",
            "upsert": true
          },
          {
            "input_id": 2,
            "value": "Security Team"
          }
        ]
      }'
```

## Response

A successful request will return a JSON object with the updated cases and their fields.

### Field description

| Parameter | Description                       |
| --------- | --------------------------------- |
| cases     | An array of updated case objects. |

| Case object | Description                       |
| ----------- | --------------------------------- |
| case_id     | The ID of the case.               |
| fields      | An array of fields for this case. |

| Field object | Description                                             |
| ------------ | ------------------------------------------------------- |
| id           | The field ID.                                           |
| value        | The value of the field.                                 |
| case_input   | An object containing the ID and name of the case input. |

### Sample response

```json
{
  "cases": [
    {
      "case_id": 42,
      "fields": [
        {
          "id": 1,
          "value": "High",
          "case_input": {
            "id": 1,
            "name": "Priority"
          }
        },
        {
          "id": 2,
          "value": "Security Team",
          "case_input": {
            "id": 2,
            "name": "Assigned Team"
          }
        }
      ]
    },
    {
      "case_id": 43,
      "fields": [
        {
          "id": 3,
          "value": "High",
          "case_input": {
            "id": 1,
            "name": "Priority"
          }
        },
        {
          "id": 4,
          "value": "Security Team",
          "case_input": {
            "id": 2,
            "name": "Assigned Team"
          }
        }
      ]
    },
    {
      "case_id": 44,
      "fields": [
        {
          "id": 5,
          "value": "High",
          "case_input": {
            "id": 1,
            "name": "Priority"
          }
        },
        {
          "id": 6,
          "value": "Security Team",
          "case_input": {
            "id": 2,
            "name": "Assigned Team"
          }
        }
      ]
    }
  ]
}
```

### Case inputs

#### Create

## Description

Creates a case input on a team.

## Request

HTTP Method: **POST**

| Parameter          | Description                                                                                                                                 |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| name               | The name of the case input.                                                                                                                 |
| input_type         | The type of the case input. One of `string`, `number`, `boolean`, or `timestamp`.                                                           |
| team_id            | The ID of the team where the case input should be added.                                                                                    |
| sensitive          | **Optional** A boolean (`true` or `false`) indicating whether the sensitive field permission is required to read or write this field.       |
| validation_type    | **Optional** The validation type for the case input. One of `options` or `regex`. Use these only with `string` inputs.                      |
| validation_options | **Optional** Validation options for the case input. For `options`, provide an array of values. For `regex`, provide a regex pattern string. |
| group_name         | **Optional** The name of the group to assign the case input to.                                                                             |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/case_inputs/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "name": "Operating system",
        "input_type": "string",
        "team_id": 1,
        "sensitive": false,
        "validation_type": "options",
        "validation_options": {
          "options": [
            "Windows",
            "MacOS",
            "Linux"
          ]
        }
      }'
```

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/case_inputs/ \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
        "name": "IP address",
        "input_type": "string",
        "team_id": 1,
        "sensitive": true,
        "validation_type": "regex",
        "validation_options": {
          "regex": "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$"
        }
      }'
```

## Response

A successful request will return a JSON object describing the case input.

### Field description

| Parameter             | Description                                                                                                              |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| id                    | The case input ID.                                                                                                       |
| name                  | The name of the case input.                                                                                              |
| input_type            | The type of the case input.                                                                                              |
| sensitive             | A boolean (`true` or `false`) indicating whether the sensitive field permission is required to read or write this field. |
| validation_type       | Validation type of the case input.                                                                                       |
| validation_options    | Validation options of the case input.                                                                                    |
| team                  | An object containing the ID and name of the team the case input belongs to.                                              |
| team_case_input_group | An object containing the name of the group the case input belongs to, if any.                                            |
| created_at            | An ISO 8601 timestamp representing when the case input was created.                                                      |
| updated_at            | An ISO 8601 timestamp representing when the case input was last updated.                                                 |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 21405,
  "name": "Operating system",
  "input_type": "string",
  "sensitive": false,
  "validation_type": "options",
  "validation_options": {
    "options": ["Windows", "MacOS", "Linux"]
  },
  "team": {
    "id": 1,
    "name": "Engineering"
  },
  "created_at": "2022-06-24T08:35:21Z",
  "updated_at": "2022-06-24T08:35:21Z"
}
```

<!-- cspell:enable -->

#### Get

## Description

Returns a case input.

## Request

HTTP Method: **GET**

| Query Parameter | Description                           |
| --------------- | ------------------------------------- |
| case_input_id   | The ID of the case input to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/case_inputs/<<case_input_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful response will return a JSON object representing the specified case input.

### Field description

| Parameter             | Description                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| id                    | The case input ID.                                                                                                        |
| name                  | The name of the case input.                                                                                               |
| key                   | The lookup key of the case input.                                                                                         |
| input_type            | The type of the case input.                                                                                               |
| sensitive             | A boolean (`true` or `false`) indicating whether the sensitive field permission is required to read or write this field.  |
| validation_type       | The validation type of the case input. One of `none`, `options`, or `regex`.                                              |
| validation_options    | An object containing validation configuration. For `options` type, contains an `options` array of accepted string values. |
| team                  | An object containing the ID and name of the team the case input belongs to.                                               |
| team_case_input_group | An object containing the name of the group the case input belongs to, if any.                                             |
| created_at            | An ISO 8601 timestamp representing when the case input was created.                                                       |
| updated_at            | An ISO 8601 timestamp representing when the case input was last updated.                                                  |

### Sample response

```json
{
  "case_input": {
    "id": 19990840,
    "name": "Priority",
    "key": "priority",
    "input_type": "string",
    "sensitive": false,
    "validation_type": "options",
    "validation_options": {
      "options": ["low", "medium", "high", "critical"]
    },
    "team": {
      "id": 1,
      "name": "Engineering Team"
    },
    "created_at": "2022-06-24T08:35:21Z",
    "updated_at": "2022-06-24T08:35:21Z"
  }
}
```

#### List

## Description

Returns a list of case inputs.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** Only include inputs from a specific team.                                              |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/case_inputs?team_id=<<team_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful response will return a list of case inputs.

### Field description

| Parameter             | Description                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| id                    | The case input ID.                                                                                                        |
| name                  | The name of the case input.                                                                                               |
| key                   | The lookup key of the case input.                                                                                         |
| input_type            | The type of the case input.                                                                                               |
| sensitive             | A boolean (`true` or `false`) indicating whether the sensitive field permission is required to read or write this field.  |
| validation_type       | The validation type of the case input. One of `none`, `options`, or `regex`.                                              |
| validation_options    | An object containing validation configuration. For `options` type, contains an `options` array of accepted string values. |
| team                  | An object containing the ID and name of the team the case input belongs to.                                               |
| team_case_input_group | An object containing the name of the group the case input belongs to, if any.                                             |
| created_at            | An ISO 8601 timestamp representing when the case input was created.                                                       |
| updated_at            | An ISO 8601 timestamp representing when the case input was last updated.                                                  |

### Sample response

```json
{
  "case_inputs": [
    {
      "id": 19990840,
      "name": "Priority",
      "key": "priority",
      "input_type": "string",
      "sensitive": false,
      "validation_type": "options",
      "validation_options": {
        "options": ["low", "medium", "high", "critical"]
      },
      "team": {
        "id": 1,
        "name": "Engineering Team"
      },
      "created_at": "2022-06-24T08:35:21Z",
      "updated_at": "2022-06-24T08:35:21Z"
    }
  ],
  "meta": {
    "current_page": "https://tenant.tines.io/api/v1/case_inputs?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Fields

##### List

## Description

Returns a list of fields for a case input.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| case_input_id   | The ID of the case input that the fields belong to.                                                 |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/case_inputs/<<case_input_id>>/fields \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request returns a JSON array describing fields for the case input.

### Field description

| Parameter  | Description                                                                                   |
| ---------- | --------------------------------------------------------------------------------------------- |
| id         | The field ID.                                                                                 |
| value      | The stored value.                                                                             |
| case       | An object containing the ID of the case.                                                      |
| case_input | An object containing the ID, name, validation_type, and validation_options of the case input. |

### Sample response

<!-- cspell:disable -->

```json
{
  "fields": [
    {
      "id": 1,
      "value": "high",
      "case": { "id": 1 },
      "case_input": {
        "id": 1,
        "name": "Priority",
        "validation_type": "options",
        "validation_options": {
          "options": ["low", "medium", "high", "critical"]
        }
      }
    },
    {
      "id": 2,
      "value": "value 2",
      "case": { "id": 2 },
      "case_input": {
        "id": 2,
        "name": "Input #2",
        "validation_type": "none",
        "validation_options": {}
      }
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/case_inputs/1/fields?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  }
}
```

<!-- cspell:enable -->

### Comments

#### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Add a comment to a case.

## Request

HTTP Method: **POST**

| Parameter    | Description                                                                                                                                                                    |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| value        | The value of the comment to be added to the case. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID. |
| author_email | **Optional** The email of the user commenting on the case.                                                                                                                     |

| Path Parameter | Description    |
| -------------- | -------------- |
| case_id        | ID of the case |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/comments \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "This is a comment on a case"
      }'
```

## Response

A successful request will return a JSON object describing the updated case

### Field description

| Parameter | Description                                                                              |
| --------- | ---------------------------------------------------------------------------------------- |
| case_id   | The case ID.                                                                             |
| id        | The ID of the record with the comment attached to the case.                              |
| action    | The action taken on the case - `commented` in this instance.                             |
| value     | The comment being added.                                                                 |
| user      | Details of the user who created the comment - id, first & last name, email & avatar url. |
| reactions | An array of objects describing the reactions and their reactants on the comment.         |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "action": "COMMENTED",
  "value": "This is a comment",
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar"
  },
  "reactions": [
    {
      "emoji": ":heart:",
      "reactants": [
        {
          "user_id": 1,
          "user_name": "John Smith",
          "reacted_at": "2024-03-12T10:06:38+00:00"
        }
      ]
    }
  ]
}
```

<!-- cspell:enable -->

#### Create

## Description

Add a comment to a case.

## Request

HTTP Method: **POST**

| Parameter    | Description                                                                                                                                                                    |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| value        | The value of the comment to be added to the case. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID. |
| author_email | **Optional** The email of the user commenting on the case. If the specified user is not assigned to the team, the author will fallback to the API key info.                    |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/comments \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "This is a comment on a case"
      }'
```

## Response

A successful request will return a JSON object describing the updated case

### Field description

| Parameter     | Description                                                                      |
| ------------- | -------------------------------------------------------------------------------- |
| case_id       | The ID of the case.                                                              |
| id            | The ID of the record with the comment attached to the case.                      |
| activity_type | The type of activity taken on the case - `commented` in this instance.           |
| value         | The comment being added.                                                         |
| created_at    | ISO 8601 Timestamp representing creation date.                                   |
| user          | Details of the user.                                                             |
| reactions     | An array of objects describing the reactions and their reactants on the comment. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "activity_type": "COMMENTED",
  "value": "This is a comment",
  "created_at": "2024-03-07T11:42:58Z",
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "reactions": [
    {
      "emoji": ":heart:",
      "reactants": [
        {
          "user_id": 1,
          "user_name": "John Smith",
          "reacted_at": "2024-03-12T10:06:38+00:00"
        }
      ]
    }
  ]
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a single comment for a case.

## Request

HTTP Method: **GET**

| Parameter  | Description            |
| ---------- | ---------------------- |
| case_id    | The ID of the case.    |
| comment_id | The ID of the comment. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/comments/<<comment_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object of the comment.

### Field description

| Parameter     | Description                                                                      |
| ------------- | -------------------------------------------------------------------------------- |
| case_id       | The case ID.                                                                     |
| id            | The ID of the record with the comment attached to the case.                      |
| activity_type | The type of activity taken on the case - `commented` in this instance.           |
| value         | The comment being added.                                                         |
| created_at    | ISO 8601 Timestamp representing creation date.                                   |
| user          | Details of the user.                                                             |
| reactions     | An array of objects describing the reactions and their reactants on the comment. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "activity_type": "COMMENTED",
  "value": "This is a comment",
  "created_at": "2024-03-07T11:42:58Z",
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "reactions": [
    {
      "emoji": ":heart:",
      "reactants": [
        {
          "user_id": 1,
          "user_name": "John Smith",
          "reacted_at": "2024-03-12T10:06:38+00:00"
        }
      ]
    }
  ]
}
```

#### Update

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Update an existing case comment.

## Request

HTTP Method: **PUT**

| Parameter | Description                    |
| --------- | ------------------------------ |
| value     | The value of the case comment. |

| Path Parameter | Description        |
| -------------- | ------------------ |
| case_id        | ID of the case.    |
| comment_id     | ID of the comment. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/comments/<<comment_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "This is an updated comment"
      }'
```

## Response

A successful request will return a JSON object describing the updated case comment.

### Field description

| Parameter | Description                                                                              |
| --------- | ---------------------------------------------------------------------------------------- |
| case_id   | The case ID.                                                                             |
| id        | The ID of the record with the comment attached to the case.                              |
| action    | The action taken on the case - `commented` in this instance.                             |
| value     | The updated case comment.                                                                |
| user      | Details of the user who created the comment - id, first & last name, email & avatar url. |
| reactions | An array of objects describing the reactions and their reactants on the comment.         |

### Sample response

```json
{
  "case_id": 42,
  "id": 7508,
  "action": "COMMENTED",
  "value": "This is an updated comment",
  "user": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar"
  },
  "reactions": [
    {
      "emoji": ":heart:",
      "reactants": [
        {
          "user_id": 1,
          "user_name": "John Smith",
          "reacted_at": "2024-03-12T10:06:38+00:00"
        }
      ]
    }
  ]
}
```

#### Update

## Description

Update an existing case comment.

## Request

HTTP Method: **PUT**

| Parameter | Description                    |
| --------- | ------------------------------ |
| value     | The value of the case comment. |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | The ID of the case.    |
| comment_id     | The ID of the comment. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/comments/<<comment_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "This is an updated comment"
      }'
```

## Response

A successful request will return a JSON object describing the updated case comment.

### Field description

| Parameter  | Description                                                                      |
| ---------- | -------------------------------------------------------------------------------- |
| case_id    | The ID of the case.                                                              |
| id         | The ID of the record with the comment attached to the case.                      |
| action     | The action taken on the case - `commented` in this instance.                     |
| value      | The updated case comment.                                                        |
| created_at | ISO 8601 Timestamp representing creation date.                                   |
| user       | Details of the user.                                                             |
| reactions  | An array of objects describing the reactions and their reactants on the comment. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

```json
{
  "case_id": 42,
  "id": 7508,
  "activity_type": "COMMENTED",
  "value": "This is an updated comment",
  "created_at": "2024-03-07T11:42:58Z",
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "reactions": [
    {
      "emoji": ":heart:",
      "reactants": [
        {
          "user_id": 1,
          "user_name": "John Smith",
          "reacted_at": "2024-03-12T10:06:38+00:00"
        }
      ]
    }
  ]
}
```

#### List

## Description

Retrieve a list of comments for a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/comments \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the comments.

### Field description

| Parameter | Description                          |
| --------- | ------------------------------------ |
| case_id   | The case ID.                         |
| comments  | An array of comments on to the case. |

| Comments parameter | Description                                                                      |
| ------------------ | -------------------------------------------------------------------------------- |
| id                 | The ID of the record with the comment attached to the case.                      |
| activity_type      | The type of activity taken on the case - `commented` in this instance.           |
| value              | The comment being added.                                                         |
| created_at         | ISO 8601 Timestamp representing creation date.                                   |
| user               | Details of the user.                                                             |
| reactions          | An array of objects describing the reactions and their reactants on the comment. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "comments": [
    {
      "id": 7508,
      "activity_type": "COMMENTED",
      "value": "This is a comment",
      "created_at": "2024-03-07T11:42:58Z",
      "user": {
        "user_id": 1,
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@tines.io",
        "avatar_url": "example.com/avatar",
        "is_service_account": false
      },
      "reactions": [
        {
          "emoji": ":heart:",
          "reactants": [
            {
              "user_id": 1,
              "user_name": "John Smith",
              "reacted_at": "2024-03-12T10:06:38+00:00"
            }
          ]
        }
      ]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/42/comments?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Delete a comment from a case. Comments can only be deleted by the original comment author or an admin.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description        |
| -------------- | ------------------ |
| case_id        | ID of the case.    |
| comment_id     | ID of the comment. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/comments/<<comment_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete

## Description

Delete a comment from a case. Comments can only be deleted by the original comment author or an admin.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | The ID of the case.    |
| comment_id     | The ID of the comment. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/comments/<<comment_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Reactions

##### Add a reaction

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Add a reaction to a comment.

## Request

HTTP Method: **POST**

| Parameter | Description                                                                                                                        |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| value     | The value of the reaction to be added to the comment (supported reaction values: `+1`, `-1`, `eyes`, `heart`, `white_check_mark`). |

| Path Parameter | Description       |
| -------------- | ----------------- |
| case_id        | ID of the case    |
| comment_id     | ID of the comment |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/comments/<<comment_id>>/add_reaction \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "heart"
      }'
```

## Response

A successful request will return a JSON object describing the updated comment with the reaction added for the authenticated user.

### Field description

| Parameter | Description                                                                              |
| --------- | ---------------------------------------------------------------------------------------- |
| case_id   | The case ID.                                                                             |
| id        | The ID of the record with the comment attached to the case.                              |
| action    | The action taken on the case - `commented` in this instance.                             |
| value     | The comment's value.                                                                     |
| user      | Details of the user who created the comment - id, first & last name, email & avatar url. |
| reactions | An array of objects describing the reactions and their reactants on the comment.         |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 126,
  "action": "COMMENTED",
  "value": "Comment value",
  "user": {
    "user_id": "1",
    "first_name": "John",
    "last_name": "Smith",
    "email": "john.smith@example.com",
    "avatar_url": "https://lh3.googleusercontent.com/a/QslEzz3Af-SGcswUnWBH7Fw-FKOAA=s96-c"
  },
  "reactions": [
    {
      "emoji": ":heart:",
      "reactants": [
        {
          "user_id": 1,
          "user_name": "John Smith",
          "reacted_at": "2024-03-12T10:06:38+00:00"
        }
      ]
    }
  ]
}
```

<!-- cspell:enable -->

##### Add a reaction

## Description

Add a reaction to a comment.

## Request

HTTP Method: **POST**

| Parameter | Description                                                                                                                        |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| value     | The value of the reaction to be added to the comment (supported reaction values: `+1`, `-1`, `eyes`, `heart`, `white_check_mark`). |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | The ID of the case.    |
| comment_id     | The ID of the comment. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/comments/<<comment_id>>/add_reaction \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "heart"
      }'
```

## Response

A successful request will return a JSON object describing the updated comment with the reaction added for the authenticated user.

### Field description

| Parameter     | Description                                                                      |
| ------------- | -------------------------------------------------------------------------------- |
| case_id       | The case ID.                                                                     |
| id            | The ID of the record with the comment attached to the case.                      |
| activity_type | The type of activity taken on the case - `commented` in this instance.           |
| value         | The comment being added.                                                         |
| created_at    | ISO 8601 Timestamp representing creation date.                                   |
| user          | Details of the user.                                                             |
| reactions     | An array of objects describing the reactions and their reactants on the comment. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 126,
  "action": "COMMENTED",
  "value": "Comment value",
  "created_at": "2024-03-07T11:42:58Z",
  "user": {
    "user_id": "1",
    "first_name": "John",
    "last_name": "Smith",
    "email": "john.smith@example.com",
    "avatar_url": "https://lh3.googleusercontent.com/a/QslEzz3Af-SGcswUnWBH7Fw-FKOAA=s96-c",
    "is_service_account": false
  },
  "reactions": [
    {
      "emoji": ":heart:",
      "reactants": [
        {
          "user_id": 1,
          "user_name": "John Smith",
          "reacted_at": "2024-03-12T10:06:38+00:00"
        }
      ]
    }
  ]
}
```

<!-- cspell:enable -->

##### Remove a reaction

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Remove a reaction from a comment.

## Request

HTTP Method: **POST**

| Parameter | Description                                                                                                                             |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| value     | The value of the reaction to be removed from the comment. (supported reaction values: `+1`, `-1`, `eyes`, `heart`, `white_check_mark`). |

| Path Parameter | Description       |
| -------------- | ----------------- |
| case_id        | ID of the case    |
| comment_id     | ID of the comment |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/comments/<<comment_id>>/remove_reaction \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "heart"
      }'
```

## Response

A successful request will return a JSON object describing the updated comment with the specified reaction removed for the authenticated user.

### Field description

| Parameter | Description                                                                              |
| --------- | ---------------------------------------------------------------------------------------- |
| case_id   | The case ID.                                                                             |
| id        | The ID of the record with the comment attached to the case.                              |
| action    | The action taken on the case - `commented` in this instance.                             |
| value     | The comment's value.                                                                     |
| user      | Details of the user who created the comment - id, first & last name, email & avatar url. |
| reactions | An array of objects describing the reactions and their reactants on the comment.         |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 126,
  "action": "COMMENTED",
  "value": "Comment value",
  "user": {
    "user_id": "1",
    "first_name": "John",
    "last_name": "Smith",
    "email": "john.smith@example.com",
    "avatar_url": "https://lh3.googleusercontent.com/a/QslEzz3Af-SGcswUnWBH7Fw-FKOAA=s96-c"
  },
  "reactions": []
}
```

<!-- cspell:enable -->

##### Remove a reaction

## Description

Remove a reaction from a comment.

## Request

HTTP Method: **POST**

| Parameter | Description                                                                                                                             |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| value     | The value of the reaction to be removed from the comment. (supported reaction values: `+1`, `-1`, `eyes`, `heart`, `white_check_mark`). |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | The ID of the case.    |
| comment_id     | The ID of the comment. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/comments/<<comment_id>>/remove_reaction \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "heart"
      }'
```

## Response

A successful request will return a JSON object describing the updated comment with the specified reaction removed for the authenticated user.

### Field description

| Parameter     | Description                                                                      |
| ------------- | -------------------------------------------------------------------------------- |
| case_id       | The case ID.                                                                     |
| id            | The ID of the record with the comment attached to the case.                      |
| activity_type | The type of activity taken on the case - `commented` in this instance.           |
| value         | The comment being added.                                                         |
| created_at    | ISO 8601 Timestamp representing creation date.                                   |
| user          | Details of the user.                                                             |
| reactions     | An array of objects describing the reactions and their reactants on the comment. |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 126,
  "action": "COMMENTED",
  "value": "Comment value",
  "created_at": "2024-03-07T11:42:58Z",
  "user": {
    "user_id": "1",
    "first_name": "John",
    "last_name": "Smith",
    "email": "john.smith@example.com",
    "avatar_url": "https://lh3.googleusercontent.com/a/QslEzz3Af-SGcswUnWBH7Fw-FKOAA=s96-c",
    "is_service_account": false
  },
  "reactions": []
}
```

<!-- cspell:enable -->

### Fields

#### Create

## Description

Add a field to a case.

## Request

HTTP Method: **POST**

| Parameter | Description                                          |
| --------- | ---------------------------------------------------- |
| input_id  | The ID of the case input to the used with the field. |
| value     | The value of the field.                              |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/fields \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "input_id": 1,
        "value": "This is a field on a case"
      }'
```

## Response

A successful request will return a JSON object describing the updated case

### Field description

| Parameter | Description           |
| --------- | --------------------- |
| case_id   | The ID of the case.   |
| field     | Details of the field. |

| Field parameter | Description                                                                                        |
| --------------- | -------------------------------------------------------------------------------------------------- |
| id              | The field ID.                                                                                      |
| value           | The value of the field.                                                                            |
| case_input      | An object containing the ID, name, key, validation_type, and validation_options of the case input. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "field": {
    "id": 1,
    "value": "high",
    "case_input": {
      "id": 1,
      "key": "priority",
      "name": "Priority",
      "validation_type": "options",
      "validation_options": {
        "options": ["low", "medium", "high", "critical"]
      }
    }
  }
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a single field for a case.

## Request

HTTP Method: **GET**

| Parameter | Description          |
| --------- | -------------------- |
| case_id   | The ID of the case.  |
| field_id  | The ID of the field. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/fields/<<field_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object of the field.

### Field description

| Parameter | Description           |
| --------- | --------------------- |
| case_id   | The case ID.          |
| field     | Details of the field. |

| Field parameter | Description                                                                                        |
| --------------- | -------------------------------------------------------------------------------------------------- |
| id              | The field ID.                                                                                      |
| value           | The value of the field.                                                                            |
| case_input      | An object containing the ID, name, key, validation_type, and validation_options of the case input. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "field": {
    "id": 1,
    "value": "high",
    "case_input": {
      "id": 1,
      "key": "priority",
      "name": "Priority",
      "validation_type": "options",
      "validation_options": {
        "options": ["low", "medium", "high", "critical"]
      }
    }
  }
}
```

#### Update

## Description

Update an existing case field.

## Request

HTTP Method: **PUT**

| Parameter | Description                  |
| --------- | ---------------------------- |
| value     | The value of the case field. |

| Path Parameter | Description          |
| -------------- | -------------------- |
| case_id        | The ID of the case.  |
| field_id       | The ID of the field. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/fields/<<field_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "value": "This is an updated field"
      }'
```

## Response

A successful request will return a JSON object describing the updated case field.

### Field description

| Parameter | Description           |
| --------- | --------------------- |
| case_id   | The ID of the case.   |
| field     | Details of the field. |

| Field parameter | Description                                                                                        |
| --------------- | -------------------------------------------------------------------------------------------------- |
| id              | The field ID.                                                                                      |
| value           | The value of the field.                                                                            |
| case_input      | An object containing the ID, name, key, validation_type, and validation_options of the case input. |

### Sample response

```json
{
  "case_id": 42,
  "field": {
    "id": 1,
    "value": "high",
    "case_input": {
      "id": 1,
      "key": "priority",
      "name": "Priority",
      "validation_type": "options",
      "validation_options": {
        "options": ["low", "medium", "high", "critical"]
      }
    }
  }
}
```

#### List

## Description

Retrieve a list of fields for a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/fields \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the fields.

### Field description

| Parameter | Description                        |
| --------- | ---------------------------------- |
| case_id   | The case ID.                       |
| fields    | An array of fields on to the case. |

| Fields parameter | Description                                                                                        |
| ---------------- | -------------------------------------------------------------------------------------------------- |
| id               | The field ID.                                                                                      |
| value            | The value of the field.                                                                            |
| case_input       | An object containing the ID, name, key, validation_type, and validation_options of the case input. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "fields": [
    {
      "id": 7508,
      "value": "high",
      "case_input": {
        "id": 1,
        "key": "priority",
        "name": "Priority",
        "validation_type": "options",
        "validation_options": {
          "options": ["low", "medium", "high", "critical"]
        }
      }
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/42/fields?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

## Description

Delete a field from a case.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description          |
| -------------- | -------------------- |
| case_id        | The ID of the case.  |
| field_id       | The ID of the field. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/fields/<<field_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

### Files

#### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Attach a file to a case.

## Request

HTTP Method: **POST**

| Parameter          | Description                                                                                                                                                                                 |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| filename           | The name of the file to be added to the case.                                                                                                                                               |
| file_contents      | Base64 encoded file contents to attach to the case.                                                                                                                                         |
| value              | **Optional** The value of the comment to be added to the case. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID. |
| author_email       | **Optional** The email of the user attaching the file to the case.                                                                                                                          |
| team_case_block_id | **Optional** The ID of the file block to attach the file to.                                                                                                                                |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/files \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "filename": "Name of file",
        "file_contents": "QmFzZTY0IGVuY29kZWQgZlsZSBjb250ZW50cw==",
        "value": "This is a comment alongside a file attachment.",
        "team_case_block_id": 42
      }'
```

## Response

A successful request will return a JSON object describing the action taken on the case - file_attached in this instance.

### Field description

| Parameter | Description                                                                                       |
| --------- | ------------------------------------------------------------------------------------------------- |
| case_id   | The case ID.                                                                                      |
| id        | The ID of the record with the file attached to the case.                                          |
| action    | The action taken on the case - `file_attached` or `file_attached_and_commented` in this instance. |
| value     | The comment added alongside file.                                                                 |
| user      | Details of the user who attached the file - id, first & last name, email & avatar url.            |
| block_id  | **Optional** The ID of the block the file was attached to, if a block ID was specified.           |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "action": "FILE_ATTACHED_AND_COMMENTED",
  "value": "This is a comment alongside a file",
  "user": {
    "id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar"
  },
  "block_id": "42"
}
```

<!-- cspell:enable -->

#### Create

## Description

Attach a file to a case.

## Request

HTTP Method: **POST**

| Parameter          | Description                                                                                                                                                                                 |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| filename           | The name of the file to be added to the case.                                                                                                                                               |
| file_contents      | Base64 encoded file contents to attach to the case.                                                                                                                                         |
| value              | **Optional** The value of the comment to be added to the case. Supports markdown and mentioning users. To mention a user, use the notation <@user-2435>, replacing '2435' with the user ID. |
| author_email       | **Optional** The email of the user attaching the file to the case.                                                                                                                          |
| team_case_block_id | **Optional** The ID of the file block to attach the file to.                                                                                                                                |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/files \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "filename": "Name of file",
        "file_contents": "QmFzZTY0IGVuY29kZWQgZlsZSBjb250ZW50cw==",
        "value": "This is a comment alongside a file attachment.",
        "team_case_block_id": 42
      }'
```

## Response

A successful request will return a JSON object describing the activity taken on the case - file_attached in this instance.

### Field description

| Parameter     | Description                                                                                                 |
| ------------- | ----------------------------------------------------------------------------------------------------------- |
| case_id       | The ID of the case.                                                                                         |
| id            | The ID of the record with the file attached to the case.                                                    |
| activity_type | The type of activity taken on the case - `file_attached` or `file_attached_and_commented` in this instance. |
| value         | The comment added alongside file.                                                                           |
| created_at    | ISO 8601 Timestamp representing creation date.                                                              |
| user          | Details of the user.                                                                                        |
| block_id      | **Optional** The ID of the block the file was attached to, if a block ID was specified.                     |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "activity_type": "FILE_ATTACHED_AND_COMMENTED",
  "value": "This is a comment alongside a file",
  "created_at": "2024-03-25T15:40:39Z",
  "file": {
    "filename": "hello.txt",
    "url": "http://<tenant-domain>/api/v2/cases/42/files/7508/download"
  },
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  },
  "block_id": "42"
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve details for a case file.

> **Note**: To get file metadata only, use the `/info` endpoint. To download the file contents, use the `/download` endpoint.

## Request

HTTP Method: **GET**

| Query Parameter | Description              |
| --------------- | ------------------------ |
| case_id         | The ID of the case.      |
| file_id         | The ID of the case file. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/files/<<file_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the details for a case file.

### Field description

| Parameter     | Description                                                                                                 |
| ------------- | ----------------------------------------------------------------------------------------------------------- |
| case_id       | The ID of the case.                                                                                         |
| id            | The ID of the record with the file attached to the case.                                                    |
| activity_type | The type of activity taken on the case - `file_attached` or `file_attached_and_commented` in this instance. |
| value         | The comment added alongside file.                                                                           |
| created_at    | ISO 8601 Timestamp representing creation date.                                                              |
| user          | Details of the user.                                                                                        |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "activity_type": "FILE_ATTACHED_AND_COMMENTED",
  "value": "This is a comment alongside a file",
  "created_at": "2024-03-25T15:40:39Z",
  "file": {
    "filename": "hello.txt",
    "url": "http://<tenant-domain>/api/v2/cases/42/files/7508/download"
  },
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  }
}
```

#### List

## Description

Retrieve a list of files for a case.

> **Note**: Each file includes a download URL. To get file metadata only, use the `/info` endpoint. To download the file contents, use the `/download` endpoint.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/files \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the details of the files for a case.

### Field description

| Parameter | Description                       |
| --------- | --------------------------------- |
| case_id   | The case ID.                      |
| files     | An array of files on to the case. |

| Files parameter | Description                                                                                                 |
| --------------- | ----------------------------------------------------------------------------------------------------------- |
| id              | The ID of the record with the comment attached to the case.                                                 |
| activity_type   | The type of activity taken on the case - `file_attached` or `file_attached_and_commented` in this instance. |
| value           | The comment added alongside file.                                                                           |
| created_at      | ISO 8601 Timestamp representing creation date.                                                              |
| user            | Details of the user.                                                                                        |
| reactions       | An array of objects describing the reactions and their reactants on the comment.                            |

| User parameter     | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "files": [
    {
      "id": 7508,
      "activity_type": "FILE_ATTACHED_AND_COMMENTED",
      "value": "This is a comment alongside a file",
      "created_at": "2024-03-25T15:40:39Z",
      "file": {
        "filename": "hello.txt",
        "url": "http://<tenant-domain>/api/v2/cases/42/files/7508/download"
      },
      "user": {
        "user_id": 1,
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@tines.io",
        "avatar_url": "example.com/avatar",
        "is_service_account": false
      }
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/42/comments?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Delete a file from a case. Files can only be deleted by the user who uploaded the file or an admin.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |
| file_id        | ID of the file. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/files/<<file_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete

## Description

Delete a file from a case. Files can only be deleted by the user who uploaded the file or an admin.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |
| file_id        | The ID of the file. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/files/<<file_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Download

##### Get

## Description

Retrieve a case file attachment. This endpoint returns the file contents for any supported file type.

## Request

HTTP Method: **GET**

| Path Parameter | Description              |
| -------------- | ------------------------ |
| case_id        | The ID of the case.      |
| file_id        | The ID of the case file. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/files/<<file_id>>/download \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the file contents with appropriate headers:

**Note**: To get file metadata only (without contents), use the `/info` endpoint instead.

- **Content-Type**: Set to the file's MIME type
- **Content-Disposition**:
  - `inline` for image files (jpg, jpeg, png, gif, bmp, svg, webp, avif, apng)
  - `attachment` for all other file types
- **Content-Security-Policy**: Prevents script execution in downloaded files

A successful request using a Tines HTTP action will return a case file as JSON:

### Field description

| Parameter             | Description                           |
| --------------------- | ------------------------------------- |
| filename              | The name of the file.                 |
| content_type          | The content type of the file.         |
| guid                  | The guid of the file.                 |
| md5                   | The md5 hash of the file.             |
| sha56                 | The sha56 of the file.                |
| sizeinbytes           | The file size in bytes.               |
| base64encodedcontents | Base 64 encoded contents of the file. |
| path                  | The file path (guid).                 |

### Sample response

```json
{
  "filename": "file",
  "content_type": "image/png",
  "guid": "78e03fc8-98cc-45dd-bf48-5a512fce6919",
  "md5": "112cb817c59986e29de77744b3052b65",
  "sha256": "807bd48b3c00aa05b0312410329c4a69c1559b1477cd17b9433e2b990f9b66b5",
  "sizeinbytes": 355,
  "base64encodedcontents": "iVBORw0KGgoANSUhEUgAABkAVO9K5CII=",
  "path": "ecfe3731-252d-48ba-8754-011fd12a9f71"
}
```

#### Info

##### Get

## Description

Retrieve metadata for a case file attachment. This endpoint returns file information without the file contents.

## Request

HTTP Method: **GET**

| Path Parameter | Description              |
| -------------- | ------------------------ |
| case_id        | The ID of the case.      |
| file_id        | The ID of the case file. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/files/<<file_id>>/info \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return the file metadata as JSON.

### Field description

| Parameter    | Description                   |
| ------------ | ----------------------------- |
| filename     | The name of the file.         |
| content_type | The content type of the file. |
| guid         | The signed ID of the file.    |
| md5          | The MD5 hash of the file.     |
| sha256       | The SHA256 hash of the file.  |
| sizeinbytes  | The file size in bytes.       |

### Sample response

```json
{
  "filename": "example.txt",
  "content_type": "text/plain",
  "guid": "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBdz09IiwiZXhwIjpudWxsLCJwdXIiOiJjb25maWd1cmF0aW9uX2tleSJ9fQ==",
  "md5": "d41d8cd98f00b204e9800998ecf8427e",
  "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "sizeinbytes": 0
}
```

### Linked cases

#### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Link two cases together by creating a new case link.

## Request

HTTP Method: **POST**

| Parameter | Description            |
| --------- | ---------------------- |
| id        | ID of the target case. |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | ID of the source case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/linked_cases \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "id": "43"
      }'
```

## Response

A successful request will return a JSON object of the source case with linked cases

### Field description

| Parameter    | Description                            |
| ------------ | -------------------------------------- |
| case_id      | The case ID.                           |
| name         | The case name.                         |
| linked_cases | An array of cases linked to this case. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Suspicious login detected",
  "linked_cases": [
    {
      "case_id": 43,
      "name": "Suspicious login detected #2"
    }
  ]
}
```

<!-- cspell:enable -->

#### Create

## Description

Link two cases together by creating a new case link.

## Request

HTTP Method: **POST**

| Parameter | Description            |
| --------- | ---------------------- |
| id        | ID of the target case. |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | ID of the source case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/linked_cases \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "id": "43"
      }'
```

## Response

A successful request will return a JSON object of the source case with the linked case.

### Field description

| Parameter   | Description                                                           |
| ----------- | --------------------------------------------------------------------- |
| case_id     | The case ID.                                                          |
| name        | The case name.                                                        |
| linked_case | An array of cases linked to this case. Includes the case id and name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Suspicious login detected",
  "linked_case": {
    "case_id": 43,
    "name": "Suspicious login detected #2"
  }
}
```

<!-- cspell:enable -->

#### List

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve the linked cases for a case.

## Request

HTTP Method: **GET**

| Parameter | Description         |
| --------- | ------------------- |
| case_id   | The ID of the case. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/linked_cases \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the linked cases.

### Field description

| Parameter    | Description                            |
| ------------ | -------------------------------------- |
| case_id      | The case ID.                           |
| name         | The case name.                         |
| linked_cases | An array of cases linked to this case. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Suspicious login detected",
  "linked_cases": [
    {
      "case_id": 43,
      "name": "Suspicious login detected #2"
    }
  ]
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve the linked cases for a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/linked_cases \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the linked cases.

### Field description

| Parameter    | Description                                                        |
| ------------ | ------------------------------------------------------------------ |
| case_id      | The ID of the case.                                                |
| name         | The case name.                                                     |
| linked_cases | An array of cases linked to this case. Including case ID and name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Suspicious login detected",
  "linked_cases": [
    {
      "case_id": 43,
      "name": "Suspicious login detected #2"
    }
  ],
  "meta": {
    "count": 1,
    "current_page": "https://<tenant-domain>/api/v2/cases/42/linked_cases?per_page=20&page=1",
    "next_page": null,
    "next_page_number": null,
    "pages": 1,
    "per_page": 20,
    "previous_page": null
  }
}
```

<!-- cspell:enable -->

#### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Unlink two cases by deleting a case link.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | ID of the case.        |
| linked_case_id | ID of the linked case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/linked_cases/<<linked_case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete

## Description

Unlink two cases by deleting a case link.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | ID of the case.        |
| linked_case_id | ID of the linked case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/linked_cases/<<linked_case_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Batch create

## Description

Batch link cases together by creating a new case link.

## Request

HTTP Method: **POST**

| Parameter | Description                                         |
| --------- | --------------------------------------------------- |
| ids       | Array of IDs of the target cases. Max length of 50. |

| Path Parameter | Description            |
| -------------- | ---------------------- |
| case_id        | ID of the source case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/linked_cases/batch \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "ids": ["43", "54", "67"]
      }'
```

## Response

A successful request will return a JSON object of the source case ID & name with the linked cases.

### Field description

| Parameter    | Description                                                            |
| ------------ | ---------------------------------------------------------------------- |
| case_id      | The case ID.                                                           |
| name         | The case name.                                                         |
| linked_cases | The array of cases linked to this case. Includes the case id and name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "name": "Suspicious login detected",
  "linked_cases": [
    {
      "case_id": 43,
      "name": "Suspicious login detected #2"
    },
    {
      "case_id": 54,
      "name": "Suspicious login detected #3"
    },
    {
      "case_id": 67,
      "name": "Suspicious login detected #4"
    }
  ]
}
```

<!-- cspell:enable -->

### Metadata

#### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Create new metadata key-value pairs for a specified case. If other metadata key-value pairs already exist on the case, this does not overwrite the existing values.

## Request

HTTP Method: **POST**

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| metadata  | Case related metadata represented as key-value pairs. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "metadata": {
          "new_key": "value"
        }
      }'
```

## Response

A successful request will return a JSON object describing the updated metadata.

### Field description

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| case_id   | The case ID.                                          |
| metadata  | Case related metadata represented as key-value pairs. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "new_key": "new value"
  }
}
```

<!-- cspell:enable -->

#### Create

## Description

Create new metadata key-value pairs for a specified case. If other metadata key-value pairs already exist on the case, this does not overwrite the existing values.

## Request

HTTP Method: **POST**

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| metadata  | Case related metadata represented as key-value pairs. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "metadata": {
          "new_key": "value"
        }
      }'
```

## Response

A successful request will return a JSON object describing the updated metadata.

### Field description

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| case_id   | The ID of the case.                                   |
| metadata  | Case related metadata represented as key-value pairs. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "new_key": "new value"
  }
}
```

<!-- cspell:enable -->

#### Get key-value pair

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a specific key-value pair from the metadata of a case.

## Request

HTTP Method: **GET**

| Parameter | Description                             |
| --------- | --------------------------------------- |
| case_id   | The ID of the case containing metadata. |
| key       | The key of the key-value pair.          |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/metadata/<<key>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the specific metadata key-value pair.

### Field description

| Parameter | Description                                                           |
| --------- | --------------------------------------------------------------------- |
| case_id   | The case ID.                                                          |
| metadata  | The case related metadata only including the specific key-value pair. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "key": "value"
  }
}
```

<!-- cspell:enable -->

#### Get key-value pair

## Description

Retrieve a specific key-value pair from the metadata of a case.

## Request

HTTP Method: **GET**

| Parameter | Description                             |
| --------- | --------------------------------------- |
| case_id   | The ID of the case containing metadata. |
| key       | The key of the key-value pair.          |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/metadata/<<key>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the specific metadata key-value pair.

### Field description

| Parameter | Description                                                           |
| --------- | --------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                   |
| metadata  | The case related metadata only including the specific key-value pair. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "key": "value"
  }
}
```

<!-- cspell:enable -->

#### Update

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Update existing metadata key-value pairs for a case.

## Request

HTTP Method: **PUT**

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| metadata  | Case related metadata represented as key-value pairs. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "metadata": {
          "key": "new value"
        }
      }'
```

## Response

A successful request will return a JSON object describing the updated metadata.

### Field description

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| case_id   | The case ID.                                          |
| metadata  | Case related metadata represented as key-value pairs. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "key": "new value"
  }
}
```

<!-- cspell:enable -->

#### Update

## Description

Update metadata key-value pairs for a case. You can update existing key-value pairs and create new ones if they don't already exist.

## Request

HTTP Method: **PUT**

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| metadata  | Case related metadata represented as key-value pairs. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "metadata": {
          "key": "new value"
        }
      }'
```

## Response

A successful request will return a JSON object describing the updated metadata.

### Field description

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| case_id   | The ID of the case.                                   |
| metadata  | Case related metadata represented as key-value pairs. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "key": "new value"
  }
}
```

<!-- cspell:enable -->

#### List

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve the metadata from a case.

## Request

HTTP Method: **GET**

| Parameter | Description                             |
| --------- | --------------------------------------- |
| case_id   | The ID of the case containing metadata. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the metadata.

### Field description

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| case_id   | The case ID.                                          |
| metadata  | Case related metadata represented as key-value pairs. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "key": "value",
    "second_key": "second value"
  }
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve the metadata from a case.

## Request

HTTP Method: **GET**

| Parameter | Description                             |
| --------- | --------------------------------------- |
| case_id   | The ID of the case containing metadata. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the metadata.

### Field description

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| case_id   | The ID of the case.                                   |
| metadata  | Case related metadata represented as key-value pairs. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "metadata": {
    "key": "value",
    "second_key": "second value"
  }
}
```

<!-- cspell:enable -->

#### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Delete existing metadata key-value pairs in a case.

## Request

HTTP Method: **DELETE**

| Parameter | Description                           |
| --------- | ------------------------------------- |
| metadata  | A list of case related metadata keys. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "metadata": ["key", "new_key"]
      }'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete

## Description

Delete existing metadata key-value pairs in a case.

## Request

HTTP Method: **DELETE**

| Parameter | Description                           |
| --------- | ------------------------------------- |
| metadata  | A list of case related metadata keys. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/metadata \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "metadata": ["key", "new_key"]
      }'
```

## Response

A successful request will return an empty response with a `204` status code.

### Notes

#### Create

## Description

Add a note to a case.

## Request

HTTP Method: **POST**

| Parameter    | Description                                                                                         |
| ------------ | --------------------------------------------------------------------------------------------------- |
| title        | The title of the note.                                                                              |
| content      | **Optional** The content of the note.                                                               |
| color        | **Optional** The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| position     | **Optional** The zero-indexed position of the note in the case.                                     |
| sensitive    | **Optional** Whether the note should be excluded from exports.                                      |
| hidden       | **Optional** Whether the note should be hidden (true or false). Defaults to false.                  |
| author_email | **Optional** The email address of the author of the note.                                           |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/notes \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "title": "Note 1",
        "content": "This is the note content.",
        "position": 0,
        "sensitive": false,
        "hidden": false,
        "author_email": "jane@tines.io"
      }'
```

## Response

A successful request will return a JSON object describing the created not

### Field description

| Parameter  | Description                                                                            |
| ---------- | -------------------------------------------------------------------------------------- |
| case_id    | The ID of the case.                                                                    |
| id         | The ID of the note record attached to the case.                                        |
| title      | The title of the note.                                                                 |
| content    | The content of the note.                                                               |
| author     | Details of the author of the note.                                                     |
| color      | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| hidden     | Whether the note is hidden (true or false).                                            |
| created_at | ISO 8601 Timestamp representing creation date and time.                                |
| updated_at | ISO 8601 Timestamp representing last updated date and time.                            |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "title": "Note title",
  "content": "This is the note content",
  "color": "white",
  "sensitive": false,
  "hidden": false,
  "created_at": "2025-01-07T11:42:58Z",
  "user": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  }
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a single note for a case.

## Request

HTTP Method: **GET**

| Parameter | Description         |
| --------- | ------------------- |
| case_id   | The ID of the case. |
| note_id   | The ID of the note. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/notes/<<note_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object of the note.

### Field description

| Parameter  | Description                                                                            |
| ---------- | -------------------------------------------------------------------------------------- |
| case_id    | The ID of the case.                                                                    |
| id         | The ID of the note record attached to the case.                                        |
| title      | The title of the note.                                                                 |
| content    | The content of the note.                                                               |
| color      | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| hidden     | Whether the note is hidden (true or false).                                            |
| author     | Details of the author of the note.                                                     |
| created_at | ISO 8601 Timestamp representing creation date and time.                                |
| updated_at | ISO 8601 Timestamp representing last updated date and time.                            |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "title": "Note title",
  "content": "This is a case note.",
  "color": "white",
  "hidden": false,
  "created_at": "2025-01-07T11:42:58Z",
  "updated_at": "2025-01-07T11:42:58Z",
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  }
}
```

<!-- cspell:enable -->

#### Update

## Description

Update an existing case note.

## Request

HTTP Method: **PUT**

| Parameter    | Description                                                                                         |
| ------------ | --------------------------------------------------------------------------------------------------- |
| title        | **Optional** The title of the case note.                                                            |
| content      | **Optional** The content of the case note.                                                          |
| color        | **Optional** The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| position     | **Optional** The zero-indexed position of the note in the case.                                     |
| sensitive    | **Optional** Whether the note should be excluded from exports.                                      |
| hidden       | **Optional** Whether the note should be hidden (true or false).                                     |
| author_email | **Optional** The email address of the author of the note.                                           |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |
| note_id        | The ID of the note. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/notes/<<note_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "content": "This is an updated note.",
        "position": 0,
        "sensitive": false,
        "hidden": false,
        "author_email": "jane@tines.io"
      }'
```

## Response

A successful request will return a JSON object describing the updated case note.

### Field description

| Parameter  | Description                                                                            |
| ---------- | -------------------------------------------------------------------------------------- |
| case_id    | The ID of the case.                                                                    |
| id         | The ID of the note record attached to the case.                                        |
| title      | The title of the note.                                                                 |
| content    | The content of the note.                                                               |
| color      | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| sensitive  | Whether the note should be excluded from exports.                                      |
| hidden     | Whether the note is hidden (true or false).                                            |
| author     | Details of the author of the note.                                                     |
| created_at | ISO 8601 Timestamp representing creation date and time.                                |
| updated_at | ISO 8601 Timestamp representing last updated date and time.                            |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "title": "Note title",
  "content": "This is an updated note.",
  "color": "white",
  "sensitive": false,
  "hidden": false,
  "created_at": "2025-01-07T11:42:58Z",
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  }
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of notes for a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/notes \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the notes.

### Field description

| Parameter | Description                                  |
| --------- | -------------------------------------------- |
| case_id   | The ID of the case.                          |
| notes     | An array of note objects attached to a case. |

| Notes Parameter | Description                                                                            |
| --------------- | -------------------------------------------------------------------------------------- |
| id              | The ID of the note record attached to the case.                                        |
| title           | The title of the note.                                                                 |
| content         | The content of the note.                                                               |
| color           | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| hidden          | Whether the note is hidden (true or false).                                            |
| author          | Details of the author of the note.                                                     |
| created_at      | ISO 8601 Timestamp representing creation date and time.                                |
| updated_at      | ISO 8601 Timestamp representing last updated date and time.                            |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "notes": [
    {
      "id": 7508,
      "title": "Note #1 title",
      "content": "This is the note content",
      "color": "white",
      "hidden": false,
      "created_at": "2025-01-07T11:42:58Z",
      "updated_at": "2025-01-07T11:42:58Z",
      "author": {
        "user_id": "1",
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@tines.io",
        "avatar_url": "example.com/avatar",
        "is_service_account": false
      }
    },
    {
      "id": 7509,
      "title": "Note #2 title",
      "content": "This is the note content",
      "color": "white",
      "hidden": false,
      "created_at": "2025-01-08T11:42:58Z",
      "updated_at": "2025-01-08T11:42:58Z",
      "author": {
        "user_id": "1",
        "first_name": "John",
        "last_name": "Doe",
        "email": "john@tines.io",
        "avatar_url": "example.com/avatar",
        "is_service_account": false
      }
    }
  ]
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a note from a case.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |
| note_id        | The ID of the note. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/notes/<<note_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
```

## Response

A successful request will return an empty response with a `204` status code.

#### Append

## Description

Append to the case note content.

## Request

HTTP Method: **POST**

| Parameter    | Description                                               |
| ------------ | --------------------------------------------------------- |
| content      | The additional content of the case note.                  |
| author_email | **Optional** The email address of the author of the note. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |
| note_id        | The ID of the note. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/notes/<<note_id>>/content \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"content": "...This is additional content to append to the existing content."}'
```

## Response

A successful request will return a JSON object describing the updated case note.

### Field description

| Parameter  | Description                                                                            |
| ---------- | -------------------------------------------------------------------------------------- |
| case_id    | The ID of the case.                                                                    |
| id         | The ID of the note record attached to the case.                                        |
| title      | The title of the note.                                                                 |
| content    | The content of the note.                                                               |
| color      | The color of the note - options: white, gold, magenta, green, blue, red, mint, indigo. |
| sensitive  | Whether the note should be excluded from exports.                                      |
| hidden     | Whether the note is hidden (true or false).                                            |
| author     | Details of the author of the note.                                                     |
| created_at | ISO 8601 Timestamp representing creation date and time.                                |
| updated_at | ISO 8601 Timestamp representing last updated date and time.                            |

| Author parameter   | Description                                          |
| ------------------ | ---------------------------------------------------- |
| user_id            | The user ID.                                         |
| first_name         | The user's first name.                               |
| last_name          | The user's last name.                                |
| email              | The user's email address.                            |
| avatar_url         | The user's avatar url.                               |
| is_service_account | Whether this user is a service account (true/false). |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "id": 7508,
  "title": "Note title",
  "content": "This is a note....This is additional content to append to the existing content.",
  "color": "white",
  "sensitive": false,
  "hidden": false,
  "created_at": "2025-01-07T11:42:58Z",
  "updated_at": "2025-01-08T06:21:39Z",
  "author": {
    "user_id": "1",
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar",
    "is_service_account": false
  }
}
```

<!-- cspell:enable -->

### Pdf

#### Get

## Description

Retrieve a PDF of a case.

## Request

HTTP Method: **GET**

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/pdf \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a pdf of the case.

### Field description

| Parameter             | Description                           |
| --------------------- | ------------------------------------- |
| filename              | The name of the file.                 |
| content_type          | The content type of the file.         |
| guid                  | The guid of the file.                 |
| md5                   | The md5 hash of the file.             |
| sha56                 | The sha56 of the file.                |
| sizeinbytes           | The file size in bytes.               |
| base64encodedcontents | Base 64 encoded contents of the file. |
| path                  | The file path (guid).                 |

### Sample response

```json
{
  "filename": "file",
  "content_type": "application/pdf",
  "guid": "78e03fc8-98cc-45dd-bf48-5a512fce6919",
  "md5": "112cb817c59986e29de77744b3052b65",
  "sha256": "807bd48b3c00aa05b0312410329c4a69c1559b1477cd17b9433e2b990f9b66b5",
  "sizeinbytes": 1355,
  "base64encodedcontents": "iVBORw0KGgoANSUhEUgAABkAVO9K5CII=",
  "path": "pdf"
}
```

### Records

#### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Add an existing record to a case.

## Request

HTTP Method: **POST**

| Parameter | Description                              |
| --------- | ---------------------------------------- |
| record_id | The ID of the record to add to the case. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/records \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "record_id": 423
      }'
```

## Response

A successful request will return a JSON object including the case and record id.

### Field description

| Parameter | Description                               |
| --------- | ----------------------------------------- |
| case_id   | The case ID that the record was added to. |
| record_id | The record ID.                            |

### Sample response

```json
{
  "case_id": 1,
  "record_id": 423
}
```

#### Create

## Description

Add an existing record to a case.

## Request

HTTP Method: **POST**

| Parameter | Description                              |
| --------- | ---------------------------------------- |
| record_id | The ID of the record to add to the case. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/records \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "record_id": 423
      }'
```

## Response

A successful request will return a JSON object including the case and record id.

### Field description

| Parameter | Description                                                            |
| --------- | ---------------------------------------------------------------------- |
| case_id   | The ID of the case that the record was added to.                       |
| record    | The record attached to the case. See the table below for more details. |

| Records parameter | Description                                                        |
| ----------------- | ------------------------------------------------------------------ |
| id                | The record ID.                                                     |
| record_type       | The record type. Includes id and name.                             |
| records           | An array of record fields. Includes the field id, name, and value. |
| created_at        | The timestamp the record was created.                              |

### Sample response

```json
{
  "case_id": 1,
  "record": {
    "id": 423,
    "record_type": { "id": 1, "name": "Alert" },
    "records": [
      {
        "id": 1871,
        "name": "Story name",
        "value": "Cases API"
      }
    ],
    "created_at": "2023-12-18T22:29:22Z"
  }
}
```

#### Get

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a single record attached to a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                |
| --------- | ------------------------------------------ |
| case_id   | The ID of the case.                        |
| record_id | The ID of the record attached to the case. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object of the record.

### Field description

| Parameter | Description                                                            |
| --------- | ---------------------------------------------------------------------- |
| case_id   | The case ID.                                                           |
| record    | The record attached to the case. See the table below for more details. |

| Records parameter | Description                                                        |
| ----------------- | ------------------------------------------------------------------ |
| id                | The record ID.                                                     |
| record_type       | The record type. Includes id and name.                             |
| records           | An array of record fields. Includes the field id, name, and value. |
| created_at        | The timestamp the record was created.                              |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "record": {
    "id": 187,
    "record_type": { "id": 1, "name": "Alert" },
    "records": [
      {
        "id": 1871,
        "name": "Story name",
        "value": "Cases API"
      }
    ],
    "created_at": "2023-12-18T22:29:22Z"
  }
}
```

#### Get

## Description

Retrieve a single record attached to a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                |
| --------- | ------------------------------------------ |
| case_id   | The ID of the case.                        |
| record_id | The ID of the record attached to the case. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object of the record.

### Field description

| Parameter | Description                                                            |
| --------- | ---------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                    |
| record    | The record attached to the case. See the table below for more details. |

| Records parameter | Description                                                        |
| ----------------- | ------------------------------------------------------------------ |
| id                | The record ID.                                                     |
| record_type       | The record type. Includes id and name.                             |
| records           | An array of record fields. Includes the field id, name, and value. |
| created_at        | The timestamp the record was created.                              |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "record": {
    "id": 187,
    "record_type": { "id": 1, "name": "Alert" },
    "records": [
      {
        "id": 1871,
        "name": "Story name",
        "value": "Cases API"
      }
    ],
    "created_at": "2023-12-18T22:29:22Z"
  }
}
```

#### List

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a list of records attached to a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/records \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the records.

### Field description

| Parameter | Description                                                                     |
| --------- | ------------------------------------------------------------------------------- |
| case_id   | The case ID.                                                                    |
| records   | An array of records attached to the case. See the table below for more details. |

| Records parameter | Description                                                        |
| ----------------- | ------------------------------------------------------------------ |
| id                | The record ID.                                                     |
| record_type       | The record type. Includes id and name.                             |
| records           | An array of record fields. Includes the field id, name, and value. |
| created_at        | The timestamp the record was created.                              |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "records": [
    {
      "id": 187,
      "record_type": { "id": 1, "name": "Alert" },
      "records": [
        {
          "id": 1871,
          "name": "Story name",
          "value": "Cases API"
        }
      ],
      "created_at": "2023-12-18T22:29:22Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/cases/42/records?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### List

## Description

Retrieve a list of records attached to a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/records \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the records.

### Field description

| Parameter | Description                                                                     |
| --------- | ------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                             |
| records   | An array of records attached to the case. See the table below for more details. |

| Records parameter | Description                                                                                                                      |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| id                | The record ID.                                                                                                                   |
| record_type       | The record type. Includes id and name.                                                                                           |
| records           | An array of records associated with the case, grouped by record type. Each record result includes the field id, name, and value. |
| created_at        | The timestamp the record was created.                                                                                            |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "records": [
    {
      "record_type_id": 55,
      "record_type_name": "Record type 2",
      "record_type_record_results": [
        {
          "id": 59,
          "results": [
            {
              "id": 59110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 59109,
              "name": "Story name",
              "value": "Alert investigation"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 58,
          "results": [
            {
              "id": 58110,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 58109,
              "name": "Story name",
              "value": "Alert investigation #1"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    },
    {
      "record_type_id": 54,
      "record_type_name": "Record type 1",
      "record_type_record_results": [
        {
          "id": 56,
          "results": [
            {
              "id": 56108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 56107,
              "name": "Story name",
              "value": "Alert investigation #2"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        },
        {
          "id": 55,
          "results": [
            {
              "id": 55108,
              "name": "Timestamp",
              "value": "2024-04-16T08:02:03Z"
            },
            {
              "id": 55107,
              "name": "Story name",
              "value": "Alert investigation #3"
            }
          ],
          "created_at": "2024-04-16T08:02:03Z"
        }
      ]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/42/records?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 4
  }
}
```

#### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Remove a record from a case.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description                            |
| -------------- | -------------------------------------- |
| case_id        | ID of the case.                        |
| record_id      | The record ID to remove from the case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete

## Description

Remove a record from a case.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description                            |
| -------------- | -------------------------------------- |
| case_id        | The ID of the case.                    |
| record_id      | The record ID to remove from the case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Subscribers

#### Create

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Subscribe to a case.

## Request

HTTP Method: **POST**

| Parameter  | Description                         |
| ---------- | ----------------------------------- |
| user_email | The email of the user to subscribe. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/subscribers \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "user_email": "jane@tines.io"
      }'
```

## Response

A successful request will return a JSON object with the case id.

### Field description

| Parameter | Description                                                           |
| --------- | --------------------------------------------------------------------- |
| case_id   | ID of the case.                                                       |
| user      | User object including, ID, first name, last name, email & avatar URL. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 7508,
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar"
  }
}
```

<!-- cspell:enable -->

#### Create

## Description

Subscribe to a case.

## Request

HTTP Method: **POST**

| Parameter  | Description                         |
| ---------- | ----------------------------------- |
| user_email | The email of the user to subscribe. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/subscribers \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "user_email": "jane@tines.io"
      }'
```

## Response

A successful request will return a JSON object with the case id.

### Field description

| Parameter | Description                                                           |
| --------- | --------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                   |
| id        | The ID of the case subscriber.                                        |
| user      | User object including, ID, first name, last name, email & avatar URL. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 7508,
  "id": 2,
  "user": {
    "user_id": 1,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@tines.io",
    "avatar_url": "example.com/avatar"
  }
}
```

<!-- cspell:enable -->

#### List

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Retrieve a list of subscribers of a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/subscribers \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the subscribers.

### Field description

| Parameter   | Description                               |
| ----------- | ----------------------------------------- |
| case_id     | The case ID.                              |
| subscribers | An array of users subscribed to the case. |

| User parameter | Description               |
| -------------- | ------------------------- |
| user_id        | The user ID.              |
| first_name     | The user's first name.    |
| last_name      | The user's last name.     |
| email          | The user's email address. |
| avatar_url     | The user's avatar url.    |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "subscribers": [
    {
      "user_id": "1",
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "avatar_url": "example.com/avatar"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/cases/42/subscribers?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### List

## Description

Retrieve a list of subscribers of a case.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| case_id   | The ID of the case.                                                                                 |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/subscribers \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object with the subscribers.

### Field description

| Parameter   | Description                               |
| ----------- | ----------------------------------------- |
| case_id     | The ID of the case.                       |
| subscribers | An array of users subscribed to the case. |

| Subscribers parameter | Description                    |
| --------------------- | ------------------------------ |
| id                    | The ID of the case subscriber. |
| user_id               | The user ID.                   |
| first_name            | The user's first name.         |
| last_name             | The user's last name.          |
| email                 | The user's email address.      |
| avatar_url            | The user's avatar url.         |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 42,
  "subscribers": [
    {
      "id": "12",
      "user_id": "1",
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@tines.io",
      "avatar_url": "example.com/avatar"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/42/subscribers?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

| ⚠️ Note                                                                     |
| --------------------------------------------------------------------------- |
| This API endpoint has been deprecated, please update to the latest version. |

## Description

Unsubscribe from a case.

## Request

HTTP Method: **DELETE**

| Parameter  | Description                           |
| ---------- | ------------------------------------- |
| user_email | The email of the user to unsubscribe. |

| Path Parameter | Description     |
| -------------- | --------------- |
| case_id        | ID of the case. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/cases/<<case_id>>/unsubscribe \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "user_email": "jane@tines.io"
      }'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete

## Description

Unsubscribe from a case.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description               |
| -------------- | ------------------------- |
| case_id        | The ID of the case.       |
| subscriber_id  | The ID of the subscriber. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/subscribers/<<subscriber_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Batch create

## Description

Batch subscribe users to a case.

## Request

HTTP Method: **POST**

| Parameter   | Description                                                  |
| ----------- | ------------------------------------------------------------ |
| user_emails | An array emails of the users to subscribe. Max length of 50. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/subscribers/batch \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "user_emails": ["jane@tines.io", "john@tines.io"]
      }'
```

## Response

A successful request will return a JSON object with the case id & subscribers.

### Field description

| Parameter   | Description                                                                                                             |
| ----------- | ----------------------------------------------------------------------------------------------------------------------- |
| case_id     | The ID of the case.                                                                                                     |
| id          | The ID of the case subscriber.                                                                                          |
| subscribers | Array of users subscribed. ID of subscriber & user object including user ID, first name, last name, email & avatar URL. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_id": 7508,
  "subscribers": [
    {
      "id": 1,
      "user": {
        "user_id": 1,
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@tines.io",
        "avatar_url": "example.com/avatar"
      }
    },
    {
      "id": 2,
      "user": {
        "user_id": 2,
        "first_name": "John",
        "last_name": "Doe",
        "email": "john@tines.io",
        "avatar_url": "example.com/avatar"
      }
    }
  ]
}
```

<!-- cspell:enable -->

### Tasks

#### Create

## Description

Create a task for a specific case.

## Request

HTTP Method: **POST**

| Parameter       | Description                                                 |
| --------------- | ----------------------------------------------------------- |
| description     | The task description.                                       |
| assignee_emails | **Optional** An array of user emails to assign to the task. |

| Path Parameter | Description                                |
| -------------- | ------------------------------------------ |
| case_id        | The ID of the case to create the task for. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/tasks \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "description": "Review security logs for suspicious activity",
  "assignee_emails": ["user1@example.com", "user2@example.com"]
}'
```

## Response

A successful request will return a JSON object representing the created task.

### Field description

| Parameter | Description              |
| --------- | ------------------------ |
| task      | The created task object. |

#### Task object fields

| Parameter   | Description                                                                |
| ----------- | -------------------------------------------------------------------------- |
| id          | The task ID.                                                               |
| description | The task description.                                                      |
| completed   | Boolean indicating whether the task is completed.                          |
| assignees   | An array of users assigned to the task.                                    |
| created_at  | ISO 8601 Timestamp representing the date and time the task was created at. |
| updated_at  | ISO 8601 Timestamp representing the date and time the task was updated at. |

#### Assignee object fields

| Parameter  | Description            |
| ---------- | ---------------------- |
| id         | The user ID.           |
| email      | The user email.        |
| first_name | The user's first name. |
| last_name  | The user's last name.  |

### Sample response

<!-- cspell:disable -->

```json
{
  "task": {
    "id": 1,
    "description": "Review security logs for suspicious activity",
    "completed": false,
    "assignees": [
      {
        "id": "123",
        "email": "user1@example.com",
        "first_name": "John",
        "last_name": "Doe"
      },
      {
        "id": "456",
        "email": "user2@example.com",
        "first_name": "Jane",
        "last_name": "Smith"
      }
    ],
    "created_at": "2024-03-25T15:40:39Z",
    "updated_at": "2024-03-25T15:40:39Z"
  }
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a single task from a specific case.

## Request

HTTP Method: **GET**

| Query Parameter | Description                     |
| --------------- | ------------------------------- |
| case_id         | The ID of the case.             |
| task_id         | The ID of the task to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/tasks/<<task_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified task.

### Field description

| Parameter | Description      |
| --------- | ---------------- |
| task      | The task object. |

#### Task object fields

| Parameter   | Description                                                                |
| ----------- | -------------------------------------------------------------------------- |
| id          | The task ID.                                                               |
| description | The task description.                                                      |
| completed   | Boolean indicating whether the task is completed.                          |
| assignees   | An array of users assigned to the task.                                    |
| created_at  | ISO 8601 Timestamp representing the date and time the task was created at. |
| updated_at  | ISO 8601 Timestamp representing the date and time the task was updated at. |

#### Assignee object fields

| Parameter  | Description            |
| ---------- | ---------------------- |
| id         | The user ID.           |
| email      | The user email.        |
| first_name | The user's first name. |
| last_name  | The user's last name.  |

### Sample response

<!-- cspell:disable -->

```json
{
  "task": {
    "id": 1,
    "description": "Review security logs",
    "completed": false,
    "assignees": [
      {
        "id": "123",
        "email": "user1@example.com",
        "first_name": "John",
        "last_name": "Doe"
      },
      {
        "id": "456",
        "email": "user2@example.com",
        "first_name": "Jane",
        "last_name": "Smith"
      }
    ],
    "created_at": "2024-03-25T15:40:39Z",
    "updated_at": "2024-03-25T15:40:39Z"
  }
}
```

<!-- cspell:enable -->

#### Update

## Description

Update a task for a specific case.

## Request

HTTP Method: **PUT**

| Parameter       | Description                                                                                       |
| --------------- | ------------------------------------------------------------------------------------------------- |
| description     | **Optional** The task description.                                                                |
| completed       | **Optional** Boolean indicating whether the task is completed.                                    |
| assignee_emails | **Optional** An array of user emails to assign to the task. This will replace existing assignees. |

| Path Parameter | Description                   |
| -------------- | ----------------------------- |
| case_id        | The ID of the case.           |
| task_id        | The ID of the task to update. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/tasks/<<task_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "description": "Review security logs for suspicious activity - Updated",
  "completed": true,
  "assignee_emails": ["user1@example.com", "user3@example.com"]
}'
```

## Response

A successful request will return a JSON object representing the updated task.

### Field description

| Parameter | Description              |
| --------- | ------------------------ |
| task      | The updated task object. |

#### Task object fields

| Parameter   | Description                                                                |
| ----------- | -------------------------------------------------------------------------- |
| id          | The task ID.                                                               |
| description | The task description.                                                      |
| completed   | Boolean indicating whether the task is completed.                          |
| assignees   | An array of users assigned to the task.                                    |
| created_at  | ISO 8601 Timestamp representing the date and time the task was created at. |
| updated_at  | ISO 8601 Timestamp representing the date and time the task was updated at. |

#### Assignee object fields

| Parameter  | Description            |
| ---------- | ---------------------- |
| id         | The user ID.           |
| email      | The user email.        |
| first_name | The user's first name. |
| last_name  | The user's last name.  |

### Sample response

<!-- cspell:disable -->

```json
{
  "task": {
    "id": 1,
    "description": "Review security logs for suspicious activity - Updated",
    "completed": true,
    "assignees": [
      {
        "id": "123",
        "email": "user1@example.com",
        "first_name": "John",
        "last_name": "Doe"
      },
      {
        "id": "789",
        "email": "user3@example.com",
        "first_name": "Bob",
        "last_name": "Johnson"
      }
    ],
    "created_at": "2024-03-25T15:40:39Z",
    "updated_at": "2024-03-25T16:25:10Z"
  }
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of tasks for a specific case.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| case_id         | The ID of the case to retrieve tasks for.                                                           |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 50.            |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  'https://<tenant-domain>/api/v2/cases/<<case_id>>/tasks?page=1&per_page=20' \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing an array of tasks for the specified case.

### Field description

| Parameter | Description                                        |
| --------- | -------------------------------------------------- |
| tasks     | An array of task objects associated with the case. |
| meta      | Pagination metadata (see below).                   |

#### Task object fields

| Parameter   | Description                                                                |
| ----------- | -------------------------------------------------------------------------- |
| id          | The task ID.                                                               |
| description | The task description.                                                      |
| completed   | Boolean indicating whether the task is completed.                          |
| assignees   | An array of users assigned to the task.                                    |
| created_at  | ISO 8601 Timestamp representing the date and time the task was created at. |
| updated_at  | ISO 8601 Timestamp representing the date and time the task was updated at. |

#### Assignee object fields

| Parameter  | Description            |
| ---------- | ---------------------- |
| id         | The user ID.           |
| email      | The user email.        |
| first_name | The user's first name. |
| last_name  | The user's last name.  |

### Sample response

<!-- cspell:disable -->

```json
{
  "tasks": [
    {
      "id": 1,
      "description": "Review security logs",
      "completed": false,
      "assignees": [
        {
          "id": "123",
          "email": "user1@example.com",
          "first_name": "John",
          "last_name": "Doe"
        },
        {
          "id": "456",
          "email": "user2@example.com",
          "first_name": "Jane",
          "last_name": "Smith"
        }
      ],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 2,
      "description": "Contact affected users",
      "completed": true,
      "assignees": [
        {
          "id": "789",
          "email": "user3@example.com",
          "first_name": "Bob",
          "last_name": "Johnson"
        }
      ],
      "created_at": "2024-03-25T15:30:20Z",
      "updated_at": "2024-03-25T16:15:45Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/tasks?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a task.

## Request

HTTP Method: **DELETE**

| Query Parameter | Description                   |
| --------------- | ----------------------------- |
| case_id         | The ID of the case.           |
| task_id         | The ID of the task to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/tasks/<<task_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the ID of the deleted task.

### Field description

| Parameter    | Description                 |
| ------------ | --------------------------- |
| destroyed_id | The ID of the deleted task. |

### Sample response

<!-- cspell:disable -->

```json
{
  "destroyed_id": "1"
}
```

<!-- cspell:enable -->

#### Batch create

## Description

Batch create tasks for a specific case.

## Request

HTTP Method: **POST**

| Parameter | Description                                                                                                                                        |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| tasks     | An array of task objects. Max length of 25. Each object must include a `description` and may optionally include an `assignee_emails` string array. |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

### Task object fields

| Parameter       | Description                                                 |
| --------------- | ----------------------------------------------------------- |
| description     | The task description.                                       |
| assignee_emails | **Optional** An array of user emails to assign to the task. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v2/cases/<<case_id>>/tasks/batch \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "tasks": [
    {
      "description": "Review security logs for suspicious activity",
      "assignee_emails": ["user1@example.com"]
    },
    {
      "description": "Update incident runbook"
    },
    {
      "description": "Close related tickets",
      "assignee_emails": ["user2@example.com", "user3@example.com"]
    }
  ]
}'
```

## Response

A successful request will return a JSON object containing an array of the created tasks.

### Field description

| Parameter | Description                       |
| --------- | --------------------------------- |
| tasks     | An array of created task objects. |

#### Task object fields

| Parameter   | Description                                                                |
| ----------- | -------------------------------------------------------------------------- |
| id          | The task ID.                                                               |
| description | The task description.                                                      |
| completed   | Boolean indicating whether the task is completed.                          |
| assignees   | An array of users assigned to the task.                                    |
| created_at  | ISO 8601 Timestamp representing the date and time the task was created at. |
| updated_at  | ISO 8601 Timestamp representing the date and time the task was updated at. |

#### Assignee object fields

| Parameter  | Description            |
| ---------- | ---------------------- |
| id         | The user ID.           |
| email      | The user email.        |
| first_name | The user's first name. |
| last_name  | The user's last name.  |

### Sample response

<!-- cspell:disable -->

```json
{
  "tasks": [
    {
      "id": 1,
      "description": "Review security logs for suspicious activity",
      "completed": false,
      "assignees": [
        {
          "id": "123",
          "email": "user1@example.com",
          "first_name": "John",
          "last_name": "Doe"
        }
      ],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 2,
      "description": "Update incident runbook",
      "completed": false,
      "assignees": [],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    },
    {
      "id": 3,
      "description": "Close related tickets",
      "completed": false,
      "assignees": [
        {
          "id": "456",
          "email": "user2@example.com",
          "first_name": "Jane",
          "last_name": "Smith"
        },
        {
          "id": "789",
          "email": "user3@example.com",
          "first_name": "Bob",
          "last_name": "Wilson"
        }
      ],
      "created_at": "2024-03-25T15:40:39Z",
      "updated_at": "2024-03-25T15:40:39Z"
    }
  ]
}
```

<!-- cspell:enable -->

## Records

### Create

## Description

Create a new record.

## Request

HTTP Method: **POST**

| Parameter      | Description                                                                                                                                                                                                              |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| record_type_id | Specifies the id of the record type for which the record is being created.                                                                                                                                               |
| field_values   | This parameter is an array comprised of objects. Each object must include a `field_id`, which identifies the record field, and a `value`, specifying the data to be assigned to that field for the newly created record. |
| case_ids       | **Optional** Specifies the case IDs to link to this record.                                                                                                                                                              |
| test_mode      | **Optional** Boolean `true` or `false` value specifying if record being created is related to test stories. Defaults to false.                                                                                           |

### Field values parameter

| Field values | Description                        |
| ------------ | ---------------------------------- |
| field_id     | The ID of the record field.        |
| value        | The value to set the record field. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/records \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
      "record_type_id": "1",
      "field_values": [{ "field_id": "1", "value": "Test" }],
      "case_ids": ["14","19"]
    }'
```

## Response

A successful request will return a JSON object representing the created record.

### Field description

| Parameter   | Description                                                               |
| ----------- | ------------------------------------------------------------------------- |
| id          | The record ID.                                                            |
| record_type | The record type.                                                          |
| test_mode   | Boolean indicator of whether the record was created in test mode.         |
| records     | The captured data for the given instance of the record type.              |
| created_at  | ISO 8601 Timestamp representing the creation date and time of the record. |
| case_ids    | The case IDs linked to this record.                                       |
| result      | The captured data in key-value format.                                    |

### Sample response

```json
{
  "id": 1973,
  "created_at": "2024-06-25T14:41:23Z",
  "record_type": {
    "id": 1097,
    "name": "Tines Sample Alert"
  },
  "test_mode": false,
  "records": [
    {
      "field_id": "5999",
      "name": "Affected user email",
      "value": "john@example.com"
    },
    {
      "field_id": "5998",
      "name": "Source",
      "value": "Login alerts"
    },
    {
      "field_id": "5997",
      "name": "Description",
      "value": "Suspicious login"
    },
    {
      "field_id": "5996",
      "name": "Severity",
      "value": "High"
    },
    {
      "field_id": "5991",
      "name": "Timestamp",
      "value": "2024-06-25 15:41:23"
    }
  ],
  "case_ids": [14, 19],
  "result": {
    "id": 1973,
    "Affected user email": "john@example.com",
    "Source": "Login alerts",
    "Description": "Suspicious login",
    "Severity": "High",
    "Timestamp": "2024-06-25 15:41:23",
    "case_ids": [14, 19],
    "updated_at": "2024-06-25T14:41:23Z"
  }
}
```

### Get

## Description

Retrieve a single record.

The record ID appears in the URL when viewing a record: `https://<tenant-domain>/records/types/{record_type_id}/results/{record_id}`, or in the response from the [List Records API](/api/records/list).

## Request

HTTP Method: **GET**

| Parameter         | Description                                                                                                                                                                          |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| record_id         | The ID of the record to retrieve.                                                                                                                                                    |
| record_type_id    | **Optional** The ID of the record type. Including this parameter may result in a faster query.                                                                                       |
| record_field_ids  | **Optional** An array of record field IDs. When provided, only the specified fields are included in the response. The `id`, `updated_at`, and `case_ids` fields are always returned. |
| resolve_artifacts | **Optional** Boolean `true` or `false` value. When `true`, Artifacts (large text) fields return the full contents instead of just the artifact ID. Defaults to `false`.              |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

Or, for faster performance, include the record type ID:

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/records/<<record_id>>?record_type_id=<<record_type_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

### Example request with record_field_ids

To return only specific fields in the response:

```bash
curl -X GET \
  "https://<tenant-domain>/api/v1/records/<<record_id>>?record_field_ids[]=<<field_id_1>>&record_field_ids[]=<<field_id_2>>" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

**Note:** All field IDs must belong to the record's record type. A `404` error is returned if any field ID does not exist, and a `422` error is returned if any field ID belongs to a different record type.

### Example request with resolve_artifacts

To return full artifact contents instead of artifact IDs:

```bash
curl -X GET \
  "https://<tenant-domain>/api/v1/records/<<record_id>>?resolve_artifacts=true" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified record.

### Artifacts (large text)

By default, Artifacts (large text) field types return a reference object containing the artifact ID and an `is_artifact` flag, rather than the data itself:

```json
{ "id": 674623, "is_artifact": true }
```

This `id` is the `artifact_id` you need to pass to the [GET Artifacts (large text) API](https://www.tines.com/api/records/artifacts/get) endpoint to retrieve the actual contents.

Alternatively, set `resolve_artifacts=true` in the request to include full artifact contents directly in the response.

### Field description

| Parameter      | Description                                                                        |
| -------------- | ---------------------------------------------------------------------------------- |
| id             | The record ID.                                                                     |
| created_at     | ISO 8601 Timestamp representing creation date and time of the record.              |
| story          | The story the record was created from.                                             |
| story_run_guid | The story run guid the record was created from.                                    |
| record_type    | The the type of record the result set was created for.                             |
| records        | The captured data for the given instance of the record type.                       |
| child_records  | An array of objects containing the record IDs for which this record is the parent. |
| parent_record  | The ID of this record's parent record.                                             |
| case_ids       | An array of case IDs linked to this record.                                        |
| result         | The captured data in key-value format.                                             |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 59,
  "created_at": "2023-06-14T15:09:02Z",
  "story": {
    "id": 8,
    "name": "Create new IOC"
  },
  "story_run_guid": "82c8e2c8-ab56-49c9-bdb9-1ea5b7fd5b2e",
  "record_type": {
    "id": 1,
    "name": "Alert"
  },
  "records": [
    {
      "name": "Story name",
      "value": "Create new IOC"
    },
    {
      "name": "Timestamp",
      "value": "2023-06-14 16:09:02"
    },
    {
      "name": "Name",
      "value": "Example"
    },
    {
      "name": "Description",
      "value": {
        "id": 674623,
        "is_artifact": true
      }
    }
  ],
  "child_records": [
    {
      "id": 9
    }
  ],
  "parent_record": {
    "id": 66
  },
  "case_ids": [4, 2],
  "result": {
    "id": 59,
    "updated_at": "2023-06-14T15:09:02Z",
    "case_ids": [4, 2],
    "Story name": "Create new IOC",
    "Timestamp": "2023-06-14 16:09:02",
    "Name": "Example",
    "Description": {
      "id": 674623,
      "is_artifact": true
    }
  }
}
```

<!-- cspell:enable -->

### Update

## Description

Updates a single record.

## Request

HTTP Method: **PUT**

| Parameter            | Description                                                                                                                                    |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| add_child_records    | **Optional** An array of record IDs to be added to the record as child records                                                                 |
| remove_child_records | **Optional** An array of record IDs to remove from the record as child records                                                                 |
| add_team_case_ids    | **Optional** An array of case IDs to be linked to the record                                                                                   |
| remove_team_case_ids | **Optional** An array of case IDs to be unlinked from the record                                                                               |
| field_values         | **Optional** An array of objects. Each object should contain a `field_id` of the field you wish to update and a `value` for the updated value. |

| Field values | Description                             |
| ------------ | --------------------------------------- |
| field_id     | The ID of the field you wish to update. |
| value        | The value to update the field with.     |

| Path Parameter | Description                     |
| -------------- | ------------------------------- |
| record_id      | The ID of the record to update. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "add_child_records": ["1"],
    "field_values": [{"field_id":"285","value":"true"}],
    "add_team_case_ids": ["14","19"]
  }'
```

## Response

A successful request will return a JSON object representing the specified record.

### Field description

| Parameter      | Description                                                                        |
| -------------- | ---------------------------------------------------------------------------------- |
| id             | The record ID.                                                                     |
| created_at     | ISO 8601 Timestamp representing the creation date and time of the record.          |
| updated_at     | ISO 8601 Timestamp representing the last update date and time of the record.       |
| story          | The story the record was created from.                                             |
| story_run_guid | The story run guid the record was created from.                                    |
| record_type    | The type of record the record was created for.                                     |
| records        | The captured data for the given instance of the record type.                       |
| child_records  | An array of objects containing the record IDs for which this record is the parent. |
| case_ids       | The case IDs linked to this record.                                                |
| result         | The captured data in key-value format.                                             |

A successful request will return a JSON object describing the updated record.

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 59,
  "created_at": "2023-06-14T15:09:02Z",
  "updated_at": "2023-06-14T15:10:32Z",
  "story": {
    "id": 8,
    "name": "Create new IOC"
  },
  "story_run_guid": "82c8e2c8-ab56-49c9-bdb9-1ea5b7fd5b2e",
  "record_type": {
    "id": 1,
    "name": "Alert"
  },
  "records": [
    {
      "field_id": "36",
      "name": "Story name",
      "value": "Create new IOC"
    },
    {
      "field_id": "37",
      "name": "Timestamp",
      "value": "2023-06-14 16:09:02"
    },
    {
      "field_id": "38",
      "name": "Name",
      "value": "Example"
    }
  ],
  "child_records": [
    {
      "id": 9,
      "records": [
        {
          "field_id": "39",
          "name": "Story name",
          "value": "Create new IOC"
        },
        {
          "field_id": "40",
          "name": "Timestamp",
          "value": "2023-06-14 16:09:02"
        },
        {
          "field_id": "41",
          "name": "Name",
          "value": "Example"
        }
      ]
    }
  ],
  "case_ids": [14, 19],
  "result": {
    "id": 59,
    "updated_at": "2023-06-14T15:10:32Z",
    "case_ids": [14, 19],
    "Story name": "Create new IOC",
    "Timestamp": "2023-06-14 16:09:02",
    "Name": "Example"
  }
}
```

<!-- cspell:enable -->

### List

## Description

Retrieve a list of records.

To find the record type ID to filter against, navigate to any record page at `https://<tenant-domain>/records`. Then, click the dropdown menu at the top right and select `Copy record type ID`. Alternatively, you may find the record type ID (and field IDs) in that same menu under `Manage record type`.

This information may also be retrieved from the [List Record Types API](/api/records/record_types/list).

![Copy record type ID](https://www.datocms-assets.com/55802/1764706143-copy-record-id-edited.png)

## Request

HTTP Method: **GET**

| Query Parameter     | Description                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| record_type_id      | The ID of the record type (optional if `record_field_ids` are present).                                                                                                                                                                                                                                                                                                                                      |
| record_field_ids    | An array of record field IDs that determines which fields (columns) are included in each record result (optional if `record_type_id` is present). When omitted and `record_type_id` is provided, all fields of the record type are returned.                                                                                                                                                                 |
| range_start         | **Optional** The start time for the time range of records (ISO 8601 timestamp).                                                                                                                                                                                                                                                                                                                              |
| range_end           | **Optional** The end time for the time range of records (ISO 8601 timestamp).                                                                                                                                                                                                                                                                                                                                |
| story_ids           | **Deprecated** **Optional** An array of _internal_ story IDs to filter the results. Cannot be used with `story_container_ids`. In test mode, it translates to test story IDs. Note: this is a legacy field and does not match the IDs in the URL of a story. It matches the IDs used in the Records view. Please use `story_container_ids` to match the behavior of other endpoints that reference Story ID. |
| story_container_ids | **Optional** An array of story IDs to filter the results. Cannot be used with `story_ids`.                                                                                                                                                                                                                                                                                                                   |
| order_direction     | **Optional** The direction to order records: `ASC` or `DESC`. Defaults to `DESC`.                                                                                                                                                                                                                                                                                                                            |
| order_field_id      | **Optional** The ID of the field used to order records. Supports custom fields as well as default fields such as Timestamp and Updated at. When omitted, records are ordered by creation date.                                                                                                                                                                                                               |
| per_page            | **Optional** Set the number of records returned per page. Defaults to 20, maximum is 500.                                                                                                                                                                                                                                                                                                                    |
| page                | **Optional** The page number of results to fetch.                                                                                                                                                                                                                                                                                                                                                            |
| filters             | **Optional** An array of objects each specifying a record filter (see the table below).                                                                                                                                                                                                                                                                                                                      |
| test_mode           | **Optional** Boolean `true` or `false` value. Specify whether to filter by records created in test stories.                                                                                                                                                                                                                                                                                                  |
| parent_ids          | **Optional** An array of parent record IDs. Returns only first-degree child records of the specified parent records. Use this to efficiently retrieve all children of one or more parent records.                                                                                                                                                                                                            |

**Important:** The `record_field_ids` parameter is different from the `field_id` used inside `filters`. The `record_field_ids` parameter controls which fields (columns) appear in each record result in the response, while `field_id` in `filters` specifies which fields to apply filter conditions to when selecting which records to return.

Supported parameters in `filters`

| Parameters | Description                                                                                                                                                                                                                                                              |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| field_id   | The record field (i.e. record column) id to apply the filter condition to.                                                                                                                                                                                               |
| operator   | `EQUAL`, `NOT_EQUAL`, `GREATER_THAN`, `GREATER_THAN_OR_EQUAL_TO`, `LESS_THAN`, `LESS_THAN_OR_EQUAL_TO`, `IS_EMPTY`, `IS_NOT_EMPTY`, `IS_TRUE`, `IS_FALSE`, `IS_ANY_OF`, or `IS_NONE_OF`. Note: `EQUAL`, `NOT_EQUAL`, `IS_ANY_OF`, and `IS_NONE_OF` are case insensitive. |
| value      | Depending on the operator, a string, number, or omitted. For `IS_ANY_OF` and `IS_NONE_OF` operators, provide an array of values instead of a single value. Note: `IS_ANY_OF` and `IS_NONE_OF` are only available for text, text fixed enums, and number fields.          |

### Example request with search filters

Sample request params:

```json
{
  "filters": [
    {
      "field_id": "5",
      "operator": "EQUAL",
      "value": "ALERT"
    }
  ],
  "record_type_id": "1",
  "range_end": "2023-07-03T00:00:00Z",
  "range_start": "2022-07-03T23:59:59Z"
}
```

Example request:

```bash
curl -v \
-X GET \
--location \
  "https://<tenant-domain>/api/v1/records?range_end=2023-07-03T00:00:00Z&range_start=2022-07-03T23:59:59Z&record_type_id=1&filters==[{"value":"ALERT","operator":"EQUAL","field_id":"5"}]" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

Tip: You can copy the cURL command of the record table and its filters you're viewing in the Tines app.

### Example request with IS_ANY_OF operator

Sample request params using `IS_ANY_OF` with an array of values:

```json
{
  "filters": [
    {
      "field_id": "5",
      "operator": "IS_ANY_OF",
      "value": ["ALERT", "WARNING", "CRITICAL"]
    }
  ],
  "record_type_id": "1"
}
```

Example request:

```bash
curl -v \
-X GET \
--location \
  "https://<tenant-domain>/api/v1/records?record_type_id=1&filters=[{\"field_id\":\"5\",\"operator\":\"IS_ANY_OF\",\"value\":[\"ALERT\",\"WARNING\",\"CRITICAL\"]}]" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

### Example request with story_container_ids

To retrieve records from specific story containers (workflows):

```bash
curl -v \
-X GET \
--location \
  "https://<tenant-domain>/api/v1/records?record_type_id=1&story_container_ids[]=123&story_container_ids[]=456" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

To retrieve records from test stories within story containers:

```bash
curl -v \
-X GET \
--location \
  "https://<tenant-domain>/api/v1/records?record_type_id=1&story_container_ids[]=123&test_mode=true" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

**Note:** You cannot specify both `story_ids` and `story_container_ids` in the same request.

### Example request with parent_ids

To retrieve all first-degree child records of specific parent records:

```bash
curl -v \
-X GET \
--location \
  "https://<tenant-domain>/api/v1/records?record_type_id=1&parent_ids[]=123&parent_ids[]=456" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

To retrieve child records of a single parent:

```bash
curl -v \
-X GET \
--location \
  "https://<tenant-domain>/api/v1/records?record_type_id=1&parent_ids[]=123" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

### Rate limit

Please be aware that usage of this endpoint is subject to a rate limit of 400 requests per minute.

## Response

A successful request will return a JSON object containing the records the requesting token has access to.

### Artifacts (large text field)

Please note that Artifacts (large text) field types do not return the Artifacts data itself, but instead return a reference object containing the artifact ID and an `is_artifact` flag:

```json
{ "id": 674623, "is_artifact": true }
```

This `id` is the `artifact_id` you need to pass to the [GET Artifacts (large text) API](https://www.tines.com/api/records/artifacts/get) endpoint to retrieve the actual contents.

### Field description

The response contains these top-level fields:

| Field          | Description                                                                            |
| -------------- | -------------------------------------------------------------------------------------- |
| record_type    | Object containing the record type information with `id` (integer) and `name` (string). |
| record_results | Array of objects, each representing a record (see below).                              |
| meta           | Object containing pagination information.                                              |

Each object in the `record_results` array contains the following fields:

| Field            | Description                                                                                                                                                                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| id               | Integer ID of the record.                                                                                                                                                                                                                              |
| updated_at       | ISO 8601 timestamp representing when the record was last updated.                                                                                                                                                                                      |
| case_ids         | Array of integer case IDs that this record is associated with.                                                                                                                                                                                         |
| parent_record    | Object with `id` (integer) for this record's parent, or `null` if this record has no parent. Same shape as [Get record](/api/records/get).                                                                                                             |
| Story name       | The name of the story this record was created in. This is a default field present on every record type.                                                                                                                                                |
| Timestamp        | The date and time the record was created. This is a default field present on every record type.                                                                                                                                                        |
| _(other fields)_ | Each remaining key-value pair corresponds to a record field. The keys are the field names and the values are the field contents. Which fields appear depends on the `record_field_ids` parameter (or all fields if only `record_type_id` is provided). |

### Sample response

<!-- cspell:disable -->

```json
{
  "record_type": {
    "id": 1,
    "name": "Alerts"
  },
  "record_results": [
    {
      "Story name": "Create new issue",
      "Timestamp": "2023-06-06 16:18:34",
      "Alert": "High",
      "updated_at": "2023-06-06T16:18:34Z",
      "case_ids": [1, 2],
      "parent_record": null,
      "id": 1
    },
    {
      "Story name": "Create new issue",
      "Timestamp": "2023-06-13 18:13:15",
      "Alert": "Medium",
      "updated_at": "2023-06-13T18:13:15Z",
      "case_ids": [1],
      "parent_record": null,
      "id": 2
    },
    {
      "Story name": "Create new issue",
      "Timestamp": "2023-06-13 18:13:16",
      "Alert": "Low",
      "updated_at": "2023-06-13T18:13:16Z",
      "case_ids": [],
      "parent_record": null,
      "id": 3
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/records?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20, // Max: 500
    "pages": 1,
    "count": 3
  }
}
```

<!-- cspell:enable -->

### Delete

## Description

Delete a record.

## Request

HTTP Method: **DELETE**

| Parameter | Description                     |
| --------- | ------------------------------- |
| record_id | The ID of the record to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Artifacts

#### Get

## Description

Retrieve an individual record artifact (large text field).

Please note that the Artifacts (large text) field types return only the ID of the Artifacts, not the data itself when using the [GET Records API](https://www.tines.com/api/records/get) or [List Records API](https://www.tines.com/api/records/list). To access the actual contents, you need to make a separate request to this endpoint using that ID as the `artifact_id` parameter.

## Request

HTTP Method: **GET**

| Path Parameter | Description                |
| -------------- | -------------------------- |
| record_id      | ID of the record.          |
| artifact_id    | ID of the record artifact. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/records/<<record_id>>/artifacts/<<artifact_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a record artifact.

### Field description

| Parameter    | Description                                                                        |
| ------------ | ---------------------------------------------------------------------------------- |
| id           | The record artifact ID.                                                            |
| value        | The record artifact's value.                                                       |
| record_field | JSON containing the id and name of the record field.                               |
| created_at   | ISO 8601 Timestamp representing the creation date and time of the record artifact. |
| updated_at   | ISO 8601 Timestamp representing the last updated date and time of record artifact. |

### Sample response

```json
{
  "id": 1,
  "value": "artifact value",
  "record_field": {
    "id": 1,
    "name": "record field name"
  },
  "created_at": "2024-02-16T15:37:39Z",
  "updated_at": "2024-02-16T15:37:39Z"
}
```

### Record types

#### Create

## Description

Create a new record type.

## Request

HTTP Method: **POST**

| Parameter             | Description                                                                                                                                                                                                                                                                                                                   |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                  | The record type name.                                                                                                                                                                                                                                                                                                         |
| description           | **Optional** A short description of the record type.                                                                                                                                                                                                                                                                          |
| team_id               | ID of team to which the record type belongs.                                                                                                                                                                                                                                                                                  |
| fields                | An array of objects describing the custom record fields defined by the user.                                                                                                                                                                                                                                                  |
| editable              | **Optional** Whether the records for this record type are editable or not.                                                                                                                                                                                                                                                    |
| ttl_days              | **Optional** Records for this record type will be deleted after this number of days. Accepted values: `0.25` (6 hours), `0.5` (12 hours), `1`, `2`, `3`, `7`, `14`, `30`, `60`, `90`, `180`, `365`, `548`, `730`, `1095`, `1460`, `1825`, `2190`, `2555`, `2920`, `3285`, `3650`.                                             |
| retention_column_name | **Optional** The timestamp column used to determine record age for TTL expiry. Accepted values are `CREATED_AT` (default) and `UPDATED_AT`. With `UPDATED_AT`, a record's age resets each time it's updated, so only records that haven't been updated within `ttl_days` are deleted. Has no effect unless `ttl_days` is set. |
| on_limit_reached      | **Optional** Behavior when the record type reaches its per-type record limit. Accepted values are `REJECT` (default; block new writes) and `EVICT_OLDEST` (delete the oldest record to make room for the new one).                                                                                                            |
| max_records_limit     | **Optional** Custom per-record-type record limit. Must be a positive integer no greater than the tenant ceiling. If omitted, the tenant default applies.                                                                                                                                                                      |

### Fields parameter

The `fields` parameter is an array of objects representing the custom record fields defined by the user. Each object within this array must include a `name` property and a `result_type` property.

The `result_type` property specifies the data type of the field. Acceptable values for `result_type` are:

- `TEXT`: For text-based fields
- `NUMBER`: For numeric fields
- `TIMESTAMP`: For date and time fields
- `BOOLEAN`: For true/false fields
- `TEXT_ENUM`: For text fields with a predefined set of values
- `ARTIFACT`: For large text fields

If the `result_type` is set to `TEXT_ENUM`, an additional `fixed_values` property must be included. This property should be an array containing the allowed values for that field.

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/record_types/
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "name": "Record type name",
      "description": "Optional description of the record type",
      "team_id": "<<META.team.id>>",
      "fields": [{ "name": "Column 1", "result_type": "TEXT" }],
      "editable": true,
      "ttl_days": 3,
      "retention_column_name": "UPDATED_AT",
      "on_limit_reached": "EVICT_OLDEST",
      "max_records_limit": 5000
    }'
```

## Response

A successful request will return a JSON object representing the created record type.

### Field description

| Parameter             | Description                                                                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                    | The record type ID.                                                                                                                                                     |
| name                  | The record type name.                                                                                                                                                   |
| description           | A short description of the record type, or null if not set.                                                                                                             |
| team_id               | ID of team to which the record type belongs.                                                                                                                            |
| editable              | Whether the records for this record type are editable or not.                                                                                                           |
| ttl_days              | Records for this record type will be deleted after this number of days.                                                                                                 |
| retention_column_name | The timestamp column used to determine record age for TTL expiry. One of `CREATED_AT` or `UPDATED_AT`. With `UPDATED_AT`, a record's age resets each time it's updated. |
| on_limit_reached      | Behavior when the record type reaches its per-type record limit. One of `REJECT` (block new writes) or `EVICT_OLDEST` (delete the oldest record to make room).          |
| max_records_limit     | Custom per-record-type record limit, or `null` if no custom limit is set (in which case the tenant default applies).                                                    |
| record_fields         | An array of objects describing the custom record fields defined by the user.                                                                                            |
| default_record_fields | An array of objects describing the default record fields (Timestamp and Story name).                                                                                    |

### Sample response

```json
{
  "id": 1,
  "name": "Record type 1",
  "description": "Optional description of the record type",
  "editable": false,
  "ttl_days": 3,
  "retention_column_name": "UPDATED_AT",
  "on_limit_reached": "EVICT_OLDEST",
  "max_records_limit": 5000,
  "record_fields": [
    {
      "id": 1,
      "name": "Field 1",
      "result_type": "TEXT",
      "fixed_values": []
    }
  ],
  "default_record_fields": [
    {
      "id": 2,
      "name": "Story name",
      "result_type": "TEXT",
      "fixed_values": []
    },
    {
      "id": 3,
      "name": "Timestamp",
      "result_type": "TIMESTAMP",
      "fixed_values": []
    }
  ]
}
```

#### Get

## Description

Retrieve a single record type.

## Request

HTTP Method: **GET**

| Parameter      | Description                            |
| -------------- | -------------------------------------- |
| record_type_id | The ID of the record type to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/record_types/<<record_type_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified record type.

### Field description

| Parameter             | Description                                                                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                    | The record type ID.                                                                                                                                                     |
| name                  | The record type name.                                                                                                                                                   |
| description           | A short description of the record type, or null if not set.                                                                                                             |
| team_id               | ID of team to which the record type belongs.                                                                                                                            |
| editable              | Whether the records for this record type are editable or not.                                                                                                           |
| ttl_days              | Records for this record type will be deleted after this number of days.                                                                                                 |
| retention_column_name | The timestamp column used to determine record age for TTL expiry. One of `CREATED_AT` or `UPDATED_AT`. With `UPDATED_AT`, a record's age resets each time it's updated. |
| on_limit_reached      | Behavior when the record type reaches its per-type record limit. One of `REJECT` (block new writes) or `EVICT_OLDEST` (delete the oldest record to make room).          |
| max_records_limit     | Custom per-record-type record limit, or `null` if no custom limit is set (in which case the tenant default applies).                                                    |
| record_fields         | An array of objects describing the custom record fields defined by the user.                                                                                            |
| default_record_fields | An array of objects describing the default record fields (Timestamp and Story name).                                                                                    |

### Sample response

```json
{
  "id": 1,
  "name": "Record type 1",
  "description": "Optional description of the record type",
  "editable": true,
  "ttl_days": 4,
  "retention_column_name": "CREATED_AT",
  "on_limit_reached": "REJECT",
  "max_records_limit": null,
  "record_fields": [
    {
      "id": 1,
      "name": "Field 1",
      "result_type": "TEXT",
      "fixed_values": []
    }
  ],
  "default_record_fields": [
    {
      "id": 2,
      "name": "Story name",
      "result_type": "TEXT",
      "fixed_values": []
    },
    {
      "id": 3,
      "name": "Timestamp",
      "result_type": "TIMESTAMP",
      "fixed_values": []
    }
  ]
}
```

#### Update

## Description

Update an existing record type. You can update the record type name, editable status, TTL settings, retention column, FIFO behavior, custom record limit, and fields.

## Request

HTTP Method: **PUT**

| Parameter             | Description                                                                                                                                                                                                                                                                                                                                                      |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| record_type_id        | The ID of the record type to update.                                                                                                                                                                                                                                                                                                                             |
| name                  | **Optional** The record type name.                                                                                                                                                                                                                                                                                                                               |
| description           | **Optional** A short description of the record type.                                                                                                                                                                                                                                                                                                             |
| editable              | **Optional** Whether the records for this record type are editable or not.                                                                                                                                                                                                                                                                                       |
| ttl_days              | **Optional** Records for this record type will be deleted after this number of days. Accepted values: `0.25` (6 hours), `0.5` (12 hours), `1`, `2`, `3`, `7`, `14`, `30`, `60`, `90`, `180`, `365`, `548`, `730`, `1095`, `1460`, `1825`, `2190`, `2555`, `2920`, `3285`, `3650`.                                                                                |
| retention_column_name | **Optional** The timestamp column used to determine record age for TTL expiry. Accepted values are `CREATED_AT` and `UPDATED_AT`. With `UPDATED_AT`, a record's age resets each time it's updated, so only records that haven't been updated within `ttl_days` are deleted. Has no effect unless `ttl_days` is set. Sending `null` while TTL is set returns 400. |
| on_limit_reached      | **Optional** Behavior when the record type reaches its per-type record limit. Accepted values are `REJECT` (block new writes) and `EVICT_OLDEST` (delete the oldest record to make room).                                                                                                                                                                        |
| max_records_limit     | **Optional** Custom per-record-type record limit. Must be a positive integer no greater than the tenant ceiling. Send `null` to clear an existing custom limit and fall back to the tenant default.                                                                                                                                                              |
| fields                | **Optional** An array of objects describing the record fields. See [Fields parameter](#fields-parameter) for behavior.                                                                                                                                                                                                                                           |
| field_ids_to_delete   | **Optional** An array of field IDs to delete. Use this to explicitly remove fields from the record type.                                                                                                                                                                                                                                                         |

### Fields parameter

The `fields` parameter is an array of objects representing the record fields. When provided, fields included in the array are created or updated. Fields not included in the array are preserved unchanged. If the `fields` parameter is omitted, not provided, or set to `null`, all existing fields remain unchanged. Providing an empty array (`[]`) will also preserve all existing fields.

To update an existing field, include its `id` along with the properties to change. To add a new field, omit the `id` and provide at least a `name` and `result_type`.

To delete fields, use the `field_ids_to_delete` parameter with an array of field IDs to remove.

The `result_type` property specifies the data type of the field. Acceptable values for `result_type` are:

- `TEXT`: For text-based fields
- `NUMBER`: For numeric fields
- `TIMESTAMP`: For date and time fields
- `BOOLEAN`: For true/false fields
- `TEXT_ENUM`: For text fields with a predefined set of values
- `ARTIFACT`: For large text fields

If the `result_type` is set to `TEXT_ENUM`, an additional `fixed_values` property must be included. This property should be an array containing the allowed values for that field. The `result_type` can only be changed between `TEXT` and `TEXT_ENUM` - other data type changes are not allowed.

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/record_types/<<record_type_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
      "name": "Updated record type name"
    }'

curl -X PUT \
  https://<tenant-domain>/api/v1/record_types/<<record_type_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
      "name": "Updated record type name",
      "description": "Updated description of the record type",
      "editable": true,
      "ttl_days": 7,
      "retention_column_name": "UPDATED_AT",
      "on_limit_reached": "EVICT_OLDEST",
      "max_records_limit": 5000,
      "fields": [
        {
          "id": 1,
          "name": "Updated Column Name",
          "result_type": "TEXT_ENUM",
          "fixed_values": ["Option A", "Option B", "Option C"]
        },
        {
          "name": "New Column",
          "result_type": "NUMBER"
        }
      ],
      "field_ids_to_delete": [2, 5]
    }'
```

## Response

A successful request will return a JSON object representing the updated record type.

### Field description

| Parameter             | Description                                                                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                    | The record type ID.                                                                                                                                                     |
| name                  | The record type name.                                                                                                                                                   |
| description           | A short description of the record type, or null if not set.                                                                                                             |
| team_id               | ID of team to which the record type belongs.                                                                                                                            |
| editable              | Whether the records for this record type are editable or not.                                                                                                           |
| ttl_days              | Records for this record type will be deleted after this number of days.                                                                                                 |
| retention_column_name | The timestamp column used to determine record age for TTL expiry. One of `CREATED_AT` or `UPDATED_AT`. With `UPDATED_AT`, a record's age resets each time it's updated. |
| on_limit_reached      | Behavior when the record type reaches its per-type record limit. One of `REJECT` (block new writes) or `EVICT_OLDEST` (delete the oldest record to make room).          |
| max_records_limit     | Custom per-record-type record limit, or `null` if no custom limit is set (in which case the tenant default applies).                                                    |
| record_fields         | An array of objects describing the custom record fields defined by the user.                                                                                            |
| default_record_fields | An array of objects describing the default record fields (Timestamp and Story name).                                                                                    |

### Sample response

```json
{
  "id": 1,
  "name": "Updated record type name",
  "description": "Updated description of the record type",
  "editable": true,
  "ttl_days": 7,
  "retention_column_name": "UPDATED_AT",
  "on_limit_reached": "EVICT_OLDEST",
  "max_records_limit": 5000,
  "team_id": 123,
  "record_fields": [
    {
      "id": 1,
      "name": "Updated Column Name",
      "result_type": "TEXT_ENUM",
      "fixed_values": ["Option A", "Option B", "Option C"]
    },
    {
      "id": 4,
      "name": "New Column",
      "result_type": "NUMBER",
      "fixed_values": []
    }
  ],
  "default_record_fields": [
    {
      "id": 2,
      "name": "Story name",
      "result_type": "TEXT",
      "fixed_values": []
    },
    {
      "id": 3,
      "name": "Timestamp",
      "result_type": "TIMESTAMP",
      "fixed_values": []
    }
  ]
}
```

#### List

## Description

Retrieve a list of record types.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| team_id         | ID of team to which the record type belongs.                              |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20. |
| page            | **Optional** The page number of results to fetch.                         |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/record_types?team_id=<<team_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing record types in the specified team.

### Field description

| Parameter             | Description                                                                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                    | The record type ID.                                                                                                                                                     |
| name                  | The record type name.                                                                                                                                                   |
| description           | A short description of the record type, or null if not set.                                                                                                             |
| team_id               | ID of team to which the record type belongs.                                                                                                                            |
| editable              | Whether the records for this record type are editable or not.                                                                                                           |
| ttl_days              | Records for this record type will be deleted after this number of days.                                                                                                 |
| retention_column_name | The timestamp column used to determine record age for TTL expiry. One of `CREATED_AT` or `UPDATED_AT`. With `UPDATED_AT`, a record's age resets each time it's updated. |
| on_limit_reached      | Behavior when the record type reaches its per-type record limit. One of `REJECT` (block new writes) or `EVICT_OLDEST` (delete the oldest record to make room).          |
| max_records_limit     | Custom per-record-type record limit, or `null` if no custom limit is set (in which case the tenant default applies).                                                    |
| record_fields         | An array of objects describing the custom record fields defined by the user.                                                                                            |
| default_record_fields | An array of objects describing the default record fields (Timestamp and Story name).                                                                                    |

### Sample response

<!-- cspell:disable -->

```json
{
  "record_types": [
    {
      "id": 1,
      "name": "Record type 1",
      "description": "Optional description of the record type",
      "team_id": 123,
      "editable": false,
      "ttl_days": 3,
      "retention_column_name": "UPDATED_AT",
      "on_limit_reached": "EVICT_OLDEST",
      "max_records_limit": 5000,
      "record_fields": [
        {
          "id": 1,
          "name": "Field 1",
          "result_type": "TEXT",
          "fixed_values": []
        }
      ],
      "default_record_fields": [
        {
          "id": 2,
          "name": "Story name",
          "result_type": "TEXT",
          "fixed_values": []
        },
        {
          "id": 3,
          "name": "Timestamp",
          "result_type": "TIMESTAMP",
          "fixed_values": []
        }
      ]
    },
    {
      "id": 2,
      "name": "Record type 2",
      "description": null,
      "team_id": 123,
      "editable": false,
      "ttl_days": null,
      "retention_column_name": "CREATED_AT",
      "on_limit_reached": "REJECT",
      "max_records_limit": null,
      "record_fields": [
        {
          "id": 4,
          "name": "Field 1",
          "result_type": "TEXT",
          "fixed_values": ["Value 1", "Value 2", "Value 3"]
        }
      ],
      "default_record_fields": [
        {
          "id": 5,
          "name": "Story name",
          "result_type": "TEXT",
          "fixed_values": []
        },
        {
          "id": 6,
          "name": "Timestamp",
          "result_type": "TIMESTAMP",
          "fixed_values": []
        }
      ]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/record_types?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 3
  }
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a record type.

Please be aware that this will also delete all associated records for this record type.

## Request

HTTP Method: **DELETE**

| Parameter      | Description                          |
| -------------- | ------------------------------------ |
| record_type_id | The ID of the record type to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/record_types/<<record_type_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Record Views

#### List

## Description

A "record view" is a saved view within a record type that includes specified filters, charts, and table layout settings. Retrieve a paginated list of record views.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                                                                 |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** Only return record views belonging to this team.                                                                               |
| record_type_id  | **Optional** Only return record views for this record type.                                                                                 |
| order           | **Optional** Order the results by one of: `NAME`, `RECENTLY_CREATED`, `RECENTLY_UPDATED`. Defaults to `NAME` when not specified or invalid. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 500.                                                   |
| page            | **Optional** The page number of results to fetch. Defaults to page 1.                                                                       |

```bash
curl -X GET \
  "https://<tenant-domain>/api/v1/record_views?team_id=1&record_type_id=3&order=RECENTLY_UPDATED" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the record views the requesting token has access to.

### Field description

| Parameter        | Description                                                                     |
| ---------------- | ------------------------------------------------------------------------------- |
| record_views     | Array of objects each representing a record view.                               |
| id               | Integer ID of the record view.                                                  |
| name             | Name of the record view.                                                        |
| team             | Object describing the team this record view belongs to.                         |
| team.id          | Integer ID of the team.                                                         |
| team.name        | Name of the team.                                                               |
| record_type      | Object describing the record type used by this record view.                     |
| record_type.id   | Integer ID of the record type.                                                  |
| record_type.name | Name of the record type.                                                        |
| created_at       | ISO 8601 timestamp representing when the record view was created.               |
| updated_at       | ISO 8601 timestamp representing when the record view was last updated.          |
| report_view      | The default view for the record view: `record_table`, `split_view`, or `chart`. |

### Sample response

<!-- cspell:disable -->

```json
{
  "record_views": [
    {
      "id": 1,
      "name": "Open cases by priority",
      "team": {
        "id": 14,
        "name": "Security Operations"
      },
      "record_type": {
        "id": 3,
        "name": "Cases"
      },
      "created_at": "2025-02-10T12:30:45Z",
      "updated_at": "2025-02-12T09:15:22Z",
      "report_view": "record_table"
    },
    {
      "id": 2,
      "name": "Incidents by source",
      "team": {
        "id": 14,
        "name": "Security Operations"
      },
      "record_type": {
        "id": 3,
        "name": "Cases"
      },
      "created_at": "2025-02-11T08:05:10Z",
      "updated_at": "2025-02-13T16:42:01Z",
      "report_view": "chart"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/record_views?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a record view.

## Request

HTTP Method: **DELETE**

| Parameter      | Description                          |
| -------------- | ------------------------------------ |
| record_view_id | The ID of the record view to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/record_views/<<record_view_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Export

## Description

Export a record view. The response can be passed to the import endpoint to recreate the record view in another team or tenant.

## Request

HTTP Method: **GET**

| Path Parameter | Description                          |
| -------------- | ------------------------------------ |
| record_view_id | The ID of the record view to export. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/record_views/<<record_view_id>>/export \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the record view in exported form.

### Field description

| Parameter      | Description                                                                                                           |
| -------------- | --------------------------------------------------------------------------------------------------------------------- |
| schema_version | The version of the export schema format.                                                                              |
| export_type    | The type of the export, "record_view".                                                                                |
| exported_at    | ISO 8601 timestamp representing when the export was generated.                                                        |
| record_view    | Object containing the record view configuration. Pass this object to the import endpoint to recreate the record view. |

### Sample response

<!-- cspell:disable -->

```json
{
  "schema_version": 1,
  "export_type": "record_view",
  "exported_at": "2025-02-10T12:30:45Z",
  "record_view": {
    "name": "Open cases by priority",
    "report_view": "chart",
    "elements": [
      {
        "id": "a1b2c3d4e5f6",
        "name": "Priority breakdown",
        "record_type_name": "Cases",
        "element_type": "chart",
        "axes": [
          {
            "direction": "x",
            "record_field_name": "priority"
          }
        ],
        "aggregation": "count",
        "rolling_date_range": null,
        "range_start": null,
        "range_end": null,
        "filters": {
          "stories": [],
          "fields": [
            {
              "value": "open",
              "operator": "equals",
              "name": "status"
            }
          ]
        },
        "order": {
          "field": null,
          "direction": null
        },
        "color_palette": "default",
        "options": {},
        "record_fields": [],
        "test_mode": false
      }
    ],
    "layout": [
      {
        "i": "a1b2c3d4e5f6",
        "x": 0,
        "y": 0,
        "w": 6,
        "h": 4
      }
    ]
  }
}
```

<!-- cspell:enable -->

#### Import

## Description

Import a record view. The import data should be obtained from the [export endpoint](/api/record_views/export).

## Request

HTTP Method: **POST**

| Body Parameter | Description                                                                                                                                       |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id        | ID of the team to which the record view should be added.                                                                                          |
| data           | JSON object representing the record view in [exported form](/api/record_views/export#sample-response).                                            |
| mode           | **Optional** Create a new record view or replace an existing one by name (`new`, `replace`). Defaults to `new`.                                   |
| name           | **Optional** Name for the record view. Defaults to the name in the export data. In `replace` mode, this specifies which existing view to replace. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/record_views/import \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "team_id": 14,
    "name": "Imported view",
    "mode": "new",
    "data": {
      "schema_version": 1,
      "record_view": {
        "name": "Open cases by priority",
        "report_view": "chart",
        "elements": [],
        "layout": []
      }
    }
  }'
```

## Response

A successful request will return a JSON object describing the newly created record view with a `201` status code.

### Field description

| Parameter  | Description                                                            |
| ---------- | ---------------------------------------------------------------------- |
| id         | Integer ID of the record view.                                         |
| name       | Name of the record view.                                               |
| team       | Object describing the team this record view belongs to.                |
| team.id    | Integer ID of the team.                                                |
| team.name  | Name of the team.                                                      |
| created_at | ISO 8601 timestamp representing when the record view was created.      |
| updated_at | ISO 8601 timestamp representing when the record view was last updated. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 42,
  "name": "Imported view",
  "team": {
    "id": 14,
    "name": "Security Operations"
  },
  "created_at": "2025-02-14T10:30:00Z",
  "updated_at": "2025-02-14T10:30:00Z"
}
```

<!-- cspell:enable -->

## Reporting

### Time saved

## Description

Returns timed and dated records of time saved by using Tines. See [Reporting](https://www.tines.com/docs/actions/reporting/) for more information.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                               |
| --------------- | ----------------------------------------------------------------------------------------- |
| start_date      | **Optional** ISO 8601 timestamp to only include records from after this datetime.         |
| end_date        | **Optional** ISO 8601 timestamp to only include records from before this datetime.        |
| date_unit       | **Optional** Arrange the data by hour, day, week, or month. Default: day                  |
| fill_gaps       | **Optional** If true, fill any empty rows with explicit 0 time saved. Default: true       |
| team_id         | **Optional** Only show time saved data for the selected team                              |
| story_id        | **Optional** Only show time saved data for the selected story                             |
| action_id       | **Optional** Only show time saved data for the selected action                            |
| per_page        | **Optional** Set the number of records returned per page. Defaults to 20, maximum is 500. |
| page            | **Optional** The page number of results to fetch.                                         |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/reporting/time_saved \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful response will return a list of time saved data according to the query parameters.

### Field description

| Parameter | Description                                                                                                                                                        |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| date      | ISO8601-compliant UTC timestamp of the date/time that time was saved in (rounded down to the nearest hour, day, week, month depending on the setting of date_unit) |
| value     | Time saved in seconds                                                                                                                                              |

### Sample response

```json
{
  "time_saved": [
    {
      "date": "2022-08-11T00:00:00Z",
      "value": 1980
    },
    {
      "date": "2022-08-12T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-13T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-14T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-15T00:00:00Z",
      "value": 4500
    },
    {
      "date": "2022-08-16T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-17T00:00:00Z",
      "value": 900
    },
    {
      "date": "2022-08-18T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-19T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-20T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-21T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-22T00:00:00Z",
      "value": 0
    },
    {
      "date": "2022-08-23T00:00:00Z",
      "value": 600
    },
    {
      "date": "2022-08-24T00:00:00Z",
      "value": 0
    }
  ],
  "meta": {
    "current_page": "http://<tenant-domain>/api/v1/reporting/time_saved?team_id=14&per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 14
  }
}
```

## Admin

### Action egress control rules

#### Create

## Description

Create a new Action egress control rule.

## Request

HTTP Method: **POST**

### Field description

| Field                | Description                                                                                                                                                                                        |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ip                   | IP address or CIDR range of the rule.                                                                                                                                                              |
| fqdn                 | The fqdn for the rule. Only IP or FQDN is allowed, not both.                                                                                                                                       |
| description          | Human-readable description of the rule.                                                                                                                                                            |
| allowed_action_types | Array of action types allowed to use the IP or FQDN. The options are "http-request" and "send-email". Any other strings will not work. **Note:** Send email allowlist is available for FQDNs only. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/admin/action_egress_control_rules \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "ip": "192.168.1.1",
      "description": "Example IP",
      "allowed_action_types": ["http-request"]
    }'
```

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/admin/action_egress_control_rules \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "fqdn": "sub.example.com",
      "description": "Example FQDN",
      "allowed_action_types": ["http-request", "send-email"]
    }'
```

<!-- cspell:enable -->

## Response

A successful request will return status 201 and the created Action egress control rule object.

See `list` for more details about the rule object.

#### Get

## Description

Get an Action egress control rule by ID.

## Request

HTTP Method: **GET**

### Field description

| Field | Description                    |
| ----- | ------------------------------ |
| id    | Action egress control rule ID. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X GET \
  https://<tenant-domain>/api/v1/admin/action_egress_control_rules/1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

<!-- cspell:enable -->

## Response

A successful request will return an object with an Action egress control rule.

### Sample response

```json
{
  "id": 1,
  "ip": "192.168.1.1",
  "fqdn": null,
  "description": "Example IP",
  "allowed_action_types": ["http-request"],
  "created_at": "2024-07-25T16:44:01Z",
  "updated_at": "2024-07-25T16:44:11Z"
}
```

#### Update

## Description

Update an existing action egress control rule.

## Request

HTTP Method: **PUT**, **PATCH**

### Field description

| Field       | Description                             |
| ----------- | --------------------------------------- |
| id          | IP access control rule ID.              |
| ip          | IP address or CIDR range of the rule.   |
| description | Human-readable description of the rule. |

### Sample requests

<!-- cspell:disable -->

```bash

curl -X PUT \
  https://<tenant-domain>/api/v1/admin/ip_access_control_rules/1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "ip": "192.168.1.1",
      "description": "Example IP"
    }'
```

```bash

curl -X PUT \
  https://<tenant-domain>/api/v1/admin/ip_access_control_rules/1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "fqdn": "tines.io",
      "description": "Example FQDN",
      "allowed_action_types": ["http-request", "send-email"]
    }'
```

<!-- cspell:enable -->

## Response

A successful request will return status 200 and the updated IP access control rule object.

See `list` for more details about the rule object.

#### List

## Description

List Action egress control rules for the tenant. Please note that these only take effect if [Action egress control](https://www.tines.com/docs/admin/action-egress-control/) is enabled.

## Request

HTTP Method: **GET**

### Field description

| Parameter | Description                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------- |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X GET \
  https://<tenant-domain>/api/v1/admin/action_egress_control_rules \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

<!-- cspell:enable -->

## Response

A successful request will return an an object with a list of Action egress control rules.

### Field description

| Parameter            | Description                                                                                                       |
| -------------------- | ----------------------------------------------------------------------------------------------------------------- |
| id                   | Action egress control rule ID.                                                                                    |
| ip                   | IP address or CIDR range of the rule.                                                                             |
| description          | Human-readable description of the rule.                                                                           |
| allowed_action_types | List of action types the IP or FQDN is allowed for. This list will include "send-email", "http-request", or both. |
| created_at           | Timestamp describing when the rule was created.                                                                   |
| updated_at           | Timestamp describing when the rule was last updated.                                                              |

### Sample response

```json
{
  "admin/action_egress_control_rules": [
    {
      "id": 1,
      "ip": "192.168.1.1",
      "fqdn": null,
      "description": "Example IP",
      "allowed_action_types": ["http-request"],
      "created_at": "2024-07-25T16:29:48Z",
      "updated_at": "2024-07-25T16:29:48Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/action_egress_control_rules?&per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

## Description

Delete an existing Action egress control rule.

## Request

HTTP Method: **DELETE**

### Field description

| Field | Description                    |
| ----- | ------------------------------ |
| id    | Action egress control rule ID. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/action_egress_control_rules/1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

<!-- cspell:enable -->

## Response

A successful request will return a 204 (no content) response.

### Custom certificate authority

#### Set

## Description

Set a custom certificate authority for use by all of your IMAP and HTTP Request actions.

## Request

HTTP Method: **PUT**

| Parameter   | Description                                                                               |
| ----------- | ----------------------------------------------------------------------------------------- |
| certificate | PEM encoded X.509 public certificate (or certificate chain) of your certificate authority |
| name        | **Optional** Name of your customer certificate authority. Default value: `default`        |

### Sample request

<!-- cspell:disable -->

```bash

curl -X PUT \
  https://<tenant-domain>/api/v1/admin/custom_certificate_authority \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
       "certificate": "<PEM encoded X.509 certificate>",
      }'
```

<!-- cspell:enable -->

## Response

A successful request will return an empty response with a `200` status code.

A failed request will return a `422` status code.

### IP Access Control

#### Create

## Description

Create a new IP access control rule.

## Request

HTTP Method: **POST**

### Field description

| Field       | Description                             |
| ----------- | --------------------------------------- |
| ip          | IP address or CIDR range of the rule.   |
| description | Human-readable description of the rule. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/admin/ip_access_control_rules \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "ip": "192.168.1.1",
      "description": "Example IP"
    }'
```

<!-- cspell:enable -->

## Response

A successful request will return status 201 and the created IP access control rule object.

See `list` for more details about the rule object.

#### Get

## Description

Get an IP access control rule by ID.

## Request

HTTP Method: **GET**

### Field description

| Field | Description                |
| ----- | -------------------------- |
| id    | IP access control rule ID. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X GET \
  https://<tenant-domain>/api/v1/admin/ip_access_control_rules/1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

<!-- cspell:enable -->

## Response

A successful request will return an object with an IP access control rule.

### Sample response

```json
{
  "id": 1,
  "ip": "192.168.1.1",
  "description": "Example IP",
  "created_at": "2024-07-25T16:44:01Z",
  "updated_at": "2024-07-25T16:44:11Z"
}
```

#### Update

## Description

Update an existing IP access control rule.

## Request

HTTP Method: **PUT**, **PATCH**

### Field description

| Field       | Description                             |
| ----------- | --------------------------------------- |
| id          | IP access control rule ID.              |
| ip          | IP address or CIDR range of the rule.   |
| description | Human-readable description of the rule. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X PUT \
  https://<tenant-domain>/api/v1/admin/ip_access_control_rules/1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
      "ip": "192.168.1.1",
      "description": "Example IP"
    }'
```

<!-- cspell:enable -->

## Response

A successful request will return status 200 and the updated IP access control rule object.

See `list` for more details about the rule object.

#### List

## Description

List IP access control rules for the tenant. Please note that these only take effect if [IP access control](https://www.tines.com/docs/admin/ip-access-control/) is enabled.

## Request

HTTP Method: **GET**

### Field description

| Parameter   | Description                                                                                         |
| ----------- | --------------------------------------------------------------------------------------------------- |
| matching_ip | **Optional** An address or CIDR to filter rules and return only matching ones.                      |
| per_page    | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page        | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X GET \
  https://<tenant-domain>/api/v1/admin/ip_access_control_rules?matching_ip=192.168.1.1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

<!-- cspell:enable -->

## Response

A successful request will return an an object with a list of IP access control rules.

### Field description

| Parameter   | Description                                          |
| ----------- | ---------------------------------------------------- |
| id          | IP access control rule ID.                           |
| ip          | IP address or CIDR range of the rule.                |
| description | Human-readable description of the rule.              |
| created_at  | Timestamp describing when the rule was created.      |
| updated_at  | Timestamp describing when the rule was last updated. |

### Sample response

```json
{
  "admin/ip_access_control_rules": [
    {
      "id": 1,
      "ip": "192.168.1.1",
      "description": "Example IP",
      "created_at": "2024-07-25T16:29:48Z",
      "updated_at": "2024-07-25T16:29:48Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/ip_access_control_rules?matching_ip=192.168.1.1&per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

## Description

Delete an existing IP access control rule.

## Request

HTTP Method: **DELETE**

### Field description

| Field | Description                |
| ----- | -------------------------- |
| id    | IP access control rule ID. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/ip_access_control_rules/1 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

<!-- cspell:enable -->

## Response

A successful request will return a 204 (no content) response.

### Jobs

#### List dead

## Description

Retrieve a list of dead jobs from the Tines tenant. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| page            | **Optional** Retrieve page number from the paginated list. Defaults to 1. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/dead_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing dead jobs in the Tines tenant.

### Field description

| Parameter       | Description                                                      |
| --------------- | ---------------------------------------------------------------- |
| id              | Job ID.                                                          |
| type            | Job type.                                                        |
| agent (Hash)    | Contains action description (AgentReceiveJob type entries only). |
| agent (Integer) | Action ID (CredentialRefreshJob type entries only).              |
| events          | Event ID.                                                        |
| retry_count     | Amount of times the job has been retried.                        |
| last_retry      | Time of the last retry.                                          |
| error_class     | Job error type.                                                  |

### Sample response

```json
{
    "admin/dead_jobs":
    [
      {
          id: 1234,
          type: "AgentReceiveJob",
          agent: {
            id: 1,
            name: "Action 01",
          },
          events: 1,
          retry_count: 5,
          last_retry: 1567399360.056226,
          error_class: ArgumentError
      },
      {
          id: 1237,
          type: "CredentialRefreshJob",
          agent: 1,
          retry_count: 5,
          last_retry: "",
          error_class: ArgumentError
      }
    ]
    "meta": {
        "current_page": "https://<tenant-domain>/api/v1/admin/dead_jobs?page=1&per_page=20",
        "previous_page": null,
        "next_page": "https://<tenant-domain>/api/v1/admin/dead_jobs?page=2&per_page=20",
        "next_page_number": 2,
        "per_page": 20,
        "pages": 0,
        "count": 0
    }
}
```

#### List in-progress

## Description

Retrieve a list of in progress jobs from the Tines tenant. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| page            | **Optional** Retrieve page number from the paginated list. Defaults to 1. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/in_progress_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing in progress jobs in the Tines tenant.

### Field description

| Parameter    | Description                                                      |
| ------------ | ---------------------------------------------------------------- |
| id           | Job ID.                                                          |
| type         | Job type.                                                        |
| agent (Hash) | Contains action description (AgentReceiveJob type entries only). |
| events       | Event ID.                                                        |
| started_at   | Job starting time.                                               |

### Sample response

```json
{
  "admin/in_progress_jobs": [
    {
      "id": 1234,
      "type": "AgentReceiveJob",
      "agent": {
        "id": 1,
        "name": "Action 01"
      },
      "events": 1,
      "started_at": 1567399360.056226
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/in_progress_jobs?page=1&per_page=20",
    "previous_page": null,
    "next_page": "https://<tenant-domain>/api/v1/admin/in_progress_jobs?page=2&per_page=20",
    "next_page_number": 2,
    "per_page": 20,
    "pages": 0,
    "count": 0
  }
}
```

#### List queued jobs

## Description

Fetch the current queue of jobs from the Tines tenant. This API endpoint is specifically for dedicated tenants. It provides a snapshot of the job queue as it exists at the moment the request is sent.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| page            | **Optional** Retrieve page number from the paginated list. Defaults to 1. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/queued_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing queued jobs in the Tines tenant.

### Field description

| Parameter    | Description                                                      |
| ------------ | ---------------------------------------------------------------- |
| id           | Job ID.                                                          |
| type         | Job type.                                                        |
| agent (Hash) | Contains action description (AgentReceiveJob type entries only). |
| events       | Event ID.                                                        |
| started_at   | Job starting time.                                               |

### Sample response

```json
{
  "admin/queued_jobs": [
    {
      "id": 1234,
      "type": "AgentReceiveJob",
      "agent": {
        "id": 1,
        "name": "Agent 01"
      },
      "events": 1,
      "retry_count": 5,
      "last_retry": 1567399360.056226,
      "error_class": "ArgumentError"
    },
    {
      "id": 1237,
      "type": "CredentialRefreshJob",
      "agent": 1,
      "retry_count": 5,
      "last_retry": "",
      "error_class": "ArgumentError"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/queued_jobs?page=1&per_page=20",
    "previous_page": null,
    "next_page": "https://<tenant-domain>/api/v1/admin/queued_jobs?page=2&per_page=20",
    "next_page_number": 2,
    "per_page": 20,
    "pages": 0,
    "count": 0
  }
}
```

#### List retry jobs

## Description

Retrieve a list of retry jobs from the Tines tenant. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| page            | **Optional** Retrieve page number from the paginated list. Defaults to 1. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/retry_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing retry jobs in the Tines tenant.

### Field description

| Parameter       | Description                                                      |
| --------------- | ---------------------------------------------------------------- |
| id              | Job ID.                                                          |
| type            | Job type.                                                        |
| agent (Hash)    | Contains action description (AgentReceiveJob type entries only). |
| agent (Integer) | Action ID (CredentialRefreshJob type entries only).              |
| events          | Event ID.                                                        |
| retry_count     | Amount of times the job has been retried.                        |
| last_retry      | Time of the last retry.                                          |
| error_class     | Job error type.                                                  |

### Sample response

```json
{
  "admin/retry_jobs": [
    {
      "id": 1234,
      "type": "AgentReceiveJob",
      "agent": {
        "id": 1,
        "name": "Action 01"
      },
      "events": 1,
      "retry_count": 5,
      "last_retry": 1567399360.056226,
      "error_class": "ArgumentError"
    },
    {
      "id": 1237,
      "type": "CredentialRefreshJob",
      "agent": 1,
      "retry_count": 5,
      "last_retry": "",
      "error_class": "ArgumentError"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/retry_jobs?page=1&per_page=20",
    "previous_page": null,
    "next_page": "https://<tenant-domain>/api/v1/admin/retry_jobs?page=2&per_page=20",
    "next_page_number": 2,
    "per_page": 20,
    "pages": 0,
    "count": 0
  }
}
```

#### Delete all dead

## Description

Delete all dead jobs from the Tines tenant. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **DELETE**

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/delete_all_dead_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete all queued

## Description

Delete all queued jobs from the Tines tenant. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **DELETE**

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/delete_all_queued_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Delete all retry

## Description

Delete all retry jobs from the Tines tenant. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **DELETE**

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/delete_all_retry_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### By Action

##### Delete dead

## Description

Delete all dead jobs by action id. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description       |
| -------------- | ----------------- |
| action-id      | ID of the action. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/actions/<<action_id>>/delete_all_dead_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

##### Delete queued

## Description

Delete all queued jobs by action id. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description       |
| -------------- | ----------------- |
| action-id      | ID of the action. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/actions/<<action_id>>/delete_all_queued_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

##### Delete retry

## Description

Delete all retry jobs by action id. This endpoint is relevant for dedicated tenants only.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description       |
| -------------- | ----------------- |
| action-id      | ID of the action. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/actions/<<action_id>>/delete_all_retry_jobs \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Roles

#### Create

## Description

Create a custom role in a Tines tenant.

## Request

HTTP Method: **POST**

| Body Parameter | Description                                              |
| -------------- | -------------------------------------------------------- |
| name           | **Required** Role name.                                  |
| description    | **Optional** A description of the role.                  |
| permissions    | **Required** A list of permissions to apply to the role. |

### Sample request

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/admin/roles \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
       "name": "MY_ROLE",
       "description": "My fantastic role",
       "permissions": ["STORY_CREATE"]
      }'
```

## Response

A successful request will return a JSON object describing the created role.

### Field description

| Parameter   | Description                                                                      |
| ----------- | -------------------------------------------------------------------------------- |
| id          | Role ID.                                                                         |
| name        | Role name.                                                                       |
| type        | Role type - custom.                                                              |
| description | Role description.                                                                |
| permissions | Array of applied permissions.                                                    |
| created_at  | Timestamp describing when the role was created.                                  |
| updated_at  | Timestamp describing when the role was last updated.                             |
| builder     | Boolean indicating whether this role grants builder / story editing permissions. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": "123",
  "name": "MY_ROLE",
  "type": "custom",
  "description": "My fantastic role",
  "permissions": ["STORY_CREATE"],
  "created_at": "2019-11-03T09:57:49.537Z",
  "updated_at": "2019-11-03T09:57:49.537Z",
  "builder": true
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve details of a specific (default or custom type) role.

## Request

HTTP Method: **GET**

| Path Parameter | Description             |
| -------------- | ----------------------- |
| role_id        | ID or name of the role. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/roles/<<role_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the role.

### Field description

| Parameter   | Description                                                                      |
| ----------- | -------------------------------------------------------------------------------- |
| id          | Role ID.                                                                         |
| name        | Role name.                                                                       |
| type        | Role type - custom or default.                                                   |
| description | Role description.                                                                |
| permissions | Array of applied permissions.                                                    |
| created_at  | Timestamp describing when the role was created.                                  |
| updated_at  | Timestamp describing when the role was last updated.                             |
| builder     | Boolean indicating whether this role grants builder / story editing permissions. |

### Sample response

```json
{
  "id": "123",
  "name": "MY_ROLE",
  "type": "custom",
  "description": "My fantastic role",
  "permissions": ["STORY_CREATE"],
  "created_at": "2019-11-03T09:57:49.537Z",
  "updated_at": "2019-11-03T09:57:49.537Z",
  "builder": true
}
```

#### Update

## Description

Update a Role.

## Request

HTTP Method: **PATCH**

| Body Parameter | Description                                                                                                                                  |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| description    | **Optional** Role description.                                                                                                               |
| permissions    | **Optional** Array of permission names, from the [list of available permissions](https://www.tines.com/docs/admin/teams/#permissions-table). |

| Path Parameter | Description             |
| -------------- | ----------------------- |
| role_id        | ID or name of the role. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X PATCH \
  https://<tenant-domain>/api/v1/admin/roles/<<role_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
       "permissions": ["CASE_CREATE"]
      }'
```

<!-- cspell:enable -->

## Response

A successful request will return a JSON object describing the updated role.

### Field description

| Parameter   | Description                                                                      |
| ----------- | -------------------------------------------------------------------------------- |
| id          | Role ID.                                                                         |
| name        | Role name.                                                                       |
| type        | Role type - custom.                                                              |
| description | Role description.                                                                |
| permissions | Array of applied permissions.                                                    |
| created_at  | Timestamp describing when the role was created.                                  |
| updated_at  | Timestamp describing when the role was last updated.                             |
| builder     | Boolean indicating whether this role grants builder / story editing permissions. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": "123",
  "name": "MY_ROLE",
  "type": "custom",
  "description": "My fantastic role",
  "permissions": ["CASE_CREATE"],
  "created_at": "2019-11-03T09:57:49.537Z",
  "updated_at": "2019-11-03T09:57:49.537Z",
  "builder": false
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of roles (both default and custom) from the Tines tenant.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/roles \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array of roles in the Tines tenant.

### Field description

| Parameter   | Description                                                                      |
| ----------- | -------------------------------------------------------------------------------- |
| id          | Role ID.                                                                         |
| name        | Role name.                                                                       |
| type        | Role type - custom or default.                                                   |
| description | Role description.                                                                |
| permissions | Array of applied permissions.                                                    |
| created_at  | Timestamp describing when the role was created.                                  |
| updated_at  | Timestamp describing when the role was last updated.                             |
| builder     | Boolean indicating whether this role grants builder / story editing permissions. |

### Sample response

<!-- cspell:disable -->

```json
{
  "admin/roles": [
    {
      "id": "TEAM_ADMIN",
      "name": "TEAM_ADMIN",
      "type": "default",
      "description": "Manage members, stories, credentials, resources and change control.",
      "permissions": ["ALLOW_ALL"],
      "created_at": null,
      "updated_at": null,
      "builder": true
    },
    {
      "id": "123",
      "name": "MY_ROLE",
      "type": "custom",
      "description": "My fantastic role",
      "permissions": ["STORY_CREATE"],
      "created_at": "2019-11-03T09:57:49.537Z",
      "updated_at": "2019-11-03T09:57:49.537Z",
      "builder": true
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/roles?page=1&per_page=20",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a specific custom role.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description             |
| -------------- | ----------------------- |
| role_id        | ID or name of the role. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/roles/<<role_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### SCIM user group mapping

#### Get

## Description

Get the SCIM user group mapping for the tenant.

## Request

HTTP Method: **GET**

### Sample request

<!-- cspell:disable -->

```bash

curl -X GET \
  https://<tenant-domain>/api/v1/admin/scim_user_group_mapping \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

<!-- cspell:enable -->

## Response

A successful request will return an object with the SCIM user group mapping.

### Sample response

```json
{
  "mappings": [
    {
      "group_name": "Managers",
      "team_name": "Analytics",
      "role_name": "TEAM_ADMIN"
    },
    {
      "group_name": "Managers",
      "team_name": "Incident Response",
      "role_name": "EDITOR"
    },
    {
      "group_name": "Analysts",
      "team_name": "Analytics",
      "role_name": "EDITOR"
    },
    {
      "group_name": "Everyone",
      "team_name": "Incident Response",
      "role_name": "VIEWER"
    }
  ]
}
```

#### Update

## Description

Update the SCIM user group mapping for the tenant. The full user group mapping must be sent, appending a new mapping is not supported.

## Request

HTTP Method: **POST**

### Sample request

<!-- cspell:disable -->

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/admin/scim_user_group_mapping \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "user_group_mapping": {
      "mappings": [
        {
          "group_name": "Managers",
          "team_name": "Analytics",
          "role_name": "TEAM_ADMIN"
        }
      ],
      "tenant_owners_group": "Managers"
    }
  }'
```

<!-- cspell:enable -->

## Response

A successful request will return status 200 and the updated SCIM user group mapping.

### Self Hosted License

#### Create

## Description

Upload and apply a new license to the tenant. A license must not already exist — use [Update](/api/admin/self-hosted-license/update/) to replace an existing license. This endpoint is only available on self-hosted tenants.

## Request

HTTP Method: **POST**

| Parameter | Description                |
| --------- | -------------------------- |
| license   | The signed license string. |

### Sample request

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/admin/license \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
       "license": "TINES-company_name-ENV::LICENSE_DATA"
      }'
```

## Response

A successful request will return a `201` status code with a JSON object describing the applied license. The response fields are the same as [Get](/api/admin/self-hosted-license/get/).

### Sample response

```json
{
  "customer_name": "Company Name",
  "unique_identifier": "UUID",
  "environment": "Production",
  "created_at": "2026-01-01T00:00:00.000Z",
  "expires_at": "2027-01-01T00:00:00Z",
  "pricing_model": "story_units",
  "users": 1000,
  "builders": 100,
  "teams": 5,
  "stories": 100,
  "story_units": 50,
  "actions": 1000000,
  "tunnels": 0,
  "daily_story_events": 50000,
  "daily_tenant_events": 100000,
  "features": ["AUDIT_LOGS", "CHANGE_CONTROL", "SSO"]
}
```

A request when a license already exists will return a `409` status code.

A request with an invalid license will return a `422` status code.

#### Get

## Description

Retrieve the current license details for the tenant. This endpoint is only available on self-hosted tenants.

## Request

HTTP Method: **GET**

### Sample request

```bash

curl -X GET \
  https://<tenant-domain>/api/v1/admin/license \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the current license.

### Field description

| Parameter           | Description                                        |
| ------------------- | -------------------------------------------------- |
| customer_name       | The customer name on the license.                  |
| unique_identifier   | The unique identifier for the license.             |
| environment         | The environment the license is issued for.         |
| created_at          | Timestamp describing when the license was created. |
| expires_at          | Timestamp describing when the license expires.     |
| pricing_model       | The pricing model for the tenant.                  |
| users               | Maximum number of users allowed.                   |
| builders            | Maximum number of builders allowed.                |
| teams               | Maximum number of teams allowed.                   |
| stories             | Maximum number of stories allowed.                 |
| story_units         | Maximum number of story units allowed.             |
| actions             | Maximum number of actions allowed.                 |
| tunnels             | Maximum number of tunnels allowed.                 |
| daily_story_events  | Maximum number of daily story events allowed.      |
| daily_tenant_events | Maximum number of daily tenant events allowed.     |
| features            | Array of feature names enabled by the license.     |

### Sample response

```json
{
  "customer_name": "Company Name",
  "unique_identifier": "UUID",
  "environment": "Production",
  "created_at": "2026-01-01T00:00:00.000Z",
  "expires_at": "2027-01-01T00:00:00Z",
  "pricing_model": "story_units",
  "users": 1000,
  "builders": 100,
  "teams": 5,
  "stories": 100,
  "story_units": 50,
  "actions": 1000000,
  "tunnels": 0,
  "daily_story_events": 50000,
  "daily_tenant_events": 100000,
  "features": ["AUDIT_LOGS", "CHANGE_CONTROL", "SSO"]
}
```

A request to a tenant with no license will return a `404` status code.

#### Update

## Description

Update and apply a new license to the tenant, replacing the existing one. A license must already exist — use [Create](/api/admin/self-hosted-license/create/) to apply the first license. This endpoint is only available on self-hosted tenants.

## Request

HTTP Method: **PUT**

| Parameter | Description                |
| --------- | -------------------------- |
| license   | The signed license string. |

### Sample request

```bash

curl -X PUT \
  https://<tenant-domain>/api/v1/admin/license \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
       "license": "TINES-company_name-ENV::LICENSE_DATA"
      }'
```

## Response

A successful request will return a `200` status code with a JSON object describing the updated license. The response fields are the same as [Get](/api/admin/self-hosted-license/get/).

### Sample response

```json
{
  "customer_name": "Company Name",
  "unique_identifier": "UUID",
  "environment": "Production",
  "created_at": "2026-01-01T00:00:00.000Z",
  "expires_at": "2027-01-01T00:00:00Z",
  "pricing_model": "story_units",
  "users": 1000,
  "builders": 100,
  "teams": 5,
  "stories": 100,
  "story_units": 50,
  "actions": 1000000,
  "tunnels": 0,
  "daily_story_events": 50000,
  "daily_tenant_events": 100000,
  "features": ["AUDIT_LOGS", "CHANGE_CONTROL", "SSO"]
}
```

A request when no license exists will return a `404` status code.

A request with an invalid license will return a `422` status code.

### Story sync destinations

#### Create

## Description

Create a new sync destination and subscribe it to one or more stories.

## Request

HTTP Method: **POST**

| Body parameter             | Description                                                                |
| -------------------------- | -------------------------------------------------------------------------- |
| destination_tenant_url     | Base URL of the destination tenant.                                        |
| destination_tenant_api_key | Tines API key used to publish to the destination tenant.                   |
| destination_team_id        | ID of the team on the destination tenant that will own the synced stories. |
| team_id                    | ID of the team on the current tenant creating the sync.                    |
| story_ids                  | Array of story IDs (from the current tenant) to sync.                      |

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/admin/sync_destinations \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "destination_tenant_url": "https://<tenant-domain>",
    "destination_tenant_api_key": "<<CREDENTIAL.tines_api_key>>",
    "destination_team_id": 42,
    "team_id": 7,
    "story_ids": [9981, 9982]
  }'
```

## Response

Returns the newly‑created sync destination object, with a `201 Created` status.

### Field description

| Parameter                     | Description                                                                  |
| ----------------------------- | ---------------------------------------------------------------------------- |
| id                            | Sync destination ID.                                                         |
| destination_tenant_url        | Base URL of the destination tenant (e.g. https://<tenant-domain>).    |
| destination_team_id           | ID of the destination team receiving the synced stories.                     |
| origin_team_id                | ID of the team on the current tenant where the sync destination was created. |
| subscriptions                 | Array of subscription objects (one per story).                               |
| subscriptions[].story.id      | ID of the story.                                                             |
| subscriptions[].story.name    | Name of the story.                                                           |
| subscriptions[].status        | Sync status. One of `synced`, `failed`, or `requires_sync`.                  |
| subscriptions[].error_message | Error details if `status` is `failed`; `null` otherwise.                     |
|                               |

### Sample response

```json
{
  "sync_destination": {
    "id": 24,
    "destination_tenant_url": "https://<tenant-domain>",
    "destination_team_id": 42,
    "origin_team_id": 7,
    "subscriptions": [
      {
        "story": { "id": 9981, "name": "High‑Priority Alerts" },
        "status": "synced",
        "error_message": null
      },
      {
        "story": { "id": 9982, "name": "Change Control Requests" },
        "status": "synced",
        "error_message": null
      }
    ]
  }
}
```

#### List

## Description

Retrieve a paginated list of sync destinations configured on the tenant.

## Request

HTTP Method: **GET**

| Query parameter | Description                              |
| --------------- | ---------------------------------------- |
| per_page        | **Optional** Number of results per page. |
| page            | **Optional** Page of results to return.  |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/sync_destinations \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request returns a JSON Array of sync destination objects.

### Field description

| Parameter                     | Description                                                                  |
| ----------------------------- | ---------------------------------------------------------------------------- |
| id                            | Sync destination ID.                                                         |
| destination_tenant_url        | Base URL of the destination tenant (e.g. https://<tenant-domain>).    |
| destination_team_id           | ID of the destination team receiving the synced stories.                     |
| origin_team_id                | ID of the team on the current tenant where the sync destination was created. |
| subscriptions                 | Array of subscription objects (one per story).                               |
| subscriptions[].story.id      | ID of the story.                                                             |
| subscriptions[].story.name    | Name of the story.                                                           |
| subscriptions[].status        | Sync status. One of `synced`, `failed`, or `requires_sync`.                  |
| subscriptions[].error_message | Error details if `status` is `failed`; `null` otherwise.                     |

### Sample response

```json
{
  "sync_destinations": [
    {
      "id": 23,
      "destination_tenant_url": "https://<tenant-domain>",
      "destination_team_id": 42,
      "origin_team_id": 7,
      "subscriptions": [
        {
          "story": {
            "id": 9981,
            "name": "Example"
          },
          "status": "synced",
          "error_message": null
        }
      ]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/sync_destinations?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

## Description

Remove a sync destination configuration.

## Request

HTTP Method: **DELETE**

| URL parameter | Description                           |
| ------------- | ------------------------------------- |
| id            | ID of the sync destination to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/sync_destinations/24 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

On success a `204 No Content` status is returned and the body is empty.

#### Manual sync

## Description

Trigger an immediate push of all subscribed stories to the destination tenant.

## Request

HTTP Method: **POST**

| URL Parameter | Description                            |
| ------------- | -------------------------------------- |
| id            | ID of the sync destination to sync to. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/admin/sync_destinations/24/manual_sync \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request returns a confirmation object, with a `200 OK` status.

### Sample response

```json
{
  "sync_initiated": true
}
```

### Templates

#### Create

## Description

Create a private template.

## Request

HTTP Method: **POST**

| Query Parameter   | Description                                                                                                                                                                                                                                                                 |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name              | Name of template.                                                                                                                                                                                                                                                           |
| description       | Template description.                                                                                                                                                                                                                                                       |
| vendor            | Vendor name.                                                                                                                                                                                                                                                                |
| product           | Product name.                                                                                                                                                                                                                                                               |
| agent_type        | Type of action to create: <br />`Agents::EmailAgent`<br />`Agents::EventTransformationAgent`<br />`Agents::HTTPRequestAgent`<br />`Agents::IMAPAgent`<br />`Agents::LLMAgent`<br />`Agents::TriggerAgent`<br />`Agents::WebhookAgent`<br />`Agents::SendToStoryAgent`<br /> |
| agent_options     | JSON object with action options.                                                                                                                                                                                                                                            |
| team_id           | **Optional** ID of team to which the template belongs.                                                                                                                                                                                                                      |
| folder_id         | **Optional** ID of the folder to place the template in. The folder must be a template folder belonging to the same team as the template.                                                                                                                                    |
| read_access       | **Optional** Control where this template can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). Default: `TEAM` when `team_id` passed, `GLOBAL` otherwise. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)             |
| shared_team_slugs | **Optional** List of teams' slugs where this template can be used. Required to set `read_access` to `SPECIFIC_TEAMS`. Default: `[]` (empty array).                                                                                                                          |
| action_inputs     | **Optional** Array of input parameters for the template. See [action_inputs structure](#action-inputs-structure) below.                                                                                                                                                     |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/admin/templates \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "name": "Test API Name",
    "description": "test",
    "agent_type": "Agents::EventTransformationAgent",
    "vendor": "API",
    "product": "API",
    "agent_options": {
        "mode": "extract",
        "matchers": [
            {
                "path": "{{.text}}",
                "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
                "to": "email_addresses"
            },
            {
                "path": "{{.text}}",
                "regexp": "https?:\\/\\/[\\S]+",
                "to": "urls"
            }
        ],
        "message": "This is an optional message"
    },
    "action_inputs": [
        {
            "name": "input_text",
            "type": "TEXT",
            "description": "The text to extract from",
            "required": true
        }
    ]
  }'
```

## Response

A successful request will return the created private template.

### Field description

| Parameter         | Description                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| id                | Template ID.                                                                                                  |
| name              | Template name.                                                                                                |
| description       | Template description.                                                                                         |
| agent_type        | Type of action associated with the template.                                                                  |
| agent_options     | Action options.                                                                                               |
| vendor            | Vendor name.                                                                                                  |
| product           | Product name.                                                                                                 |
| team_id           | ID of team to which the template belongs.                                                                     |
| folder_id         | ID of the folder containing the template, or `null` if the template is not in a folder.                       |
| read_access       | Control where this template can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                 |
| shared_team_slugs | List of teams' slugs where this template can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty. |
| action_inputs     | **Optional** Array of input parameters for the template. See details below.                                   |

### action_inputs structure

Each object in the `action_inputs` array can have the following fields:

| Field         | Description                                                                                                     |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| name          | **Required** Input parameter name.                                                                              |
| type          | **Required** Input type. One of: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`, `OBJECT`, `OPTION`, `CODE`, `CREDENTIAL`. |
| required      | Whether the input is required. Defaults to `false`. Must be `true` for `CREDENTIAL` type.                       |
| description   | **Optional** Description of the input parameter.                                                                |
| default_value | **Optional** Default value for the input.                                                                       |
| data          | **Optional** Type-specific configuration object. See below.                                                     |

The `data` object varies by type:

_Note: The `data` field is only applicable to `OBJECT`, `OPTION` and `CODE` types. Other types do not use this field._

| Type   | data fields                                                               |
| ------ | ------------------------------------------------------------------------- |
| OBJECT | `json_schema`: JSON schema for validation.                                |
| OPTION | `options`: Array of option values. `multi_select_enabled`: Boolean.       |
| CODE   | `code_language`: Language for syntax highlighting (e.g., `HTML`, `JSON`). |

#### Type-specific default_value types

| Type       | Expected default_value type           |
| ---------- | ------------------------------------- |
| TEXT       | String                                |
| NUMBER     | Integer (or formula string)           |
| BOOLEAN    | Boolean (or formula string)           |
| DATE       | String                                |
| OBJECT     | Any valid JSON value                  |
| OPTION     | Array (or formula string)             |
| CODE       | String                                |
| CREDENTIAL | Not supported (field must be omitted) |

_Formula strings start with `=` (e.g., `=INPUT.name`) or `\\=` for escaped formulas._

#### CREDENTIAL type restrictions

`CREDENTIAL` inputs only accept `name`, `type`, and `required` fields. The `description`, `default_value`, and `data` fields are not permitted and will cause a validation error if included.

### Validation constraints

The following constraints are enforced when creating a template with `action_inputs`:

- **Maximum of 300** action inputs per template.
- **Only one** `CREDENTIAL` input is allowed per template.
- Input **names must be unique** across all action inputs.
- `CREDENTIAL` inputs must have `required` set to `true`.

### Sample response

```json
{
  "id": 472463921472463921,
  "name": "Test API Name",
  "description": "test",
  "agent_type": "Agents::EventTransformationAgent",
  "agent_options": {
    "mode": "extract",
    "matchers": [
      {
        "path": "",
        "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
        "to": "email_addresses"
      },
      {
        "path": "",
        "regexp": "https?:\\/\\/[\\S]+",
        "to": "urls"
      }
    ],
    "message": "This is an optional message"
  },
  "vendor": "API",
  "product": "API",
  "team_id": 1,
  "folder_id": 42,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "action_inputs": [
    {
      "name": "input_text",
      "type": "TEXT",
      "description": "The text to extract from",
      "required": true
    }
  ]
}
```

#### Get

## Description

Retrieve a private template

## Request

HTTP Method: **GET**

| Path Parameter | Description      |
| -------------- | ---------------- |
| template_id    | The template id. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/templates/<<template_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a private template.

### Field description

| Parameter         | Description                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| id                | Template ID.                                                                                                  |
| name              | Template name.                                                                                                |
| description       | Template description.                                                                                         |
| agent_type        | Type of action associated with the template.                                                                  |
| agent_options     | Action options.                                                                                               |
| vendor            | Vendor name.                                                                                                  |
| product           | Product name.                                                                                                 |
| team_id           | ID of team to which the template belongs.                                                                     |
| folder_id         | ID of the folder containing the template, or `null` if the template is not in a folder.                       |
| read_access       | Control where this template can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                 |
| shared_team_slugs | List of teams' slugs where this template can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty. |
| action_inputs     | **Optional** Array of input parameters for the template. See details below.                                   |

### action_inputs structure

Each object in the `action_inputs` array can have the following fields:

| Field         | Description                                                                                                     |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| name          | **Required** Input parameter name.                                                                              |
| type          | **Required** Input type. One of: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`, `OBJECT`, `OPTION`, `CODE`, `CREDENTIAL`. |
| required      | Whether the input is required. Defaults to `false`. Must be `true` for `CREDENTIAL` type.                       |
| description   | **Optional** Description of the input parameter.                                                                |
| default_value | **Optional** Default value for the input.                                                                       |
| data          | **Optional** Type-specific configuration object. See below.                                                     |

The `data` object varies by type:

_Note: The `data` field is only applicable to `OBJECT`, `OPTION` and `CODE` types. Other types do not use this field._

| Type   | data fields                                                               |
| ------ | ------------------------------------------------------------------------- |
| OBJECT | `json_schema`: JSON schema for validation.                                |
| OPTION | `options`: Array of option values. `multi_select_enabled`: Boolean.       |
| CODE   | `code_language`: Language for syntax highlighting (e.g., `HTML`, `JSON`). |

### Sample response

```json
{
  "id": "472463921472463921",
  "name": "Extract some text",
  "description": "test",
  "agent_type": "Agents::EventTransformationAgent",
  "agent_options": {
    "mode": "extract",
    "matchers": [
      {
        "path": "",
        "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
        "to": "email_addresses"
      },
      {
        "path": "",
        "regexp": "https?:\\/\\/[\\S]+",
        "to": "urls"
      }
    ],
    "message": "This is an optional message"
  },
  "vendor": "API",
  "product": "API",
  "team_id": 1,
  "folder_id": 42,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "action_inputs": [
    {
      "name": "input_text",
      "type": "TEXT",
      "description": "The text to extract from",
      "required": true,
      "default_value": "Sample text"
    }
  ]
}
```

#### Update

## Description

Update a private template.

## Request

HTTP Method: **PUT**

| Query Parameter   | Description                                                                                                                                                                                                                                                                 |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name              | Name of template.                                                                                                                                                                                                                                                           |
| description       | Template description.                                                                                                                                                                                                                                                       |
| vendor            | Vendor name.                                                                                                                                                                                                                                                                |
| product           | Product name.                                                                                                                                                                                                                                                               |
| agent_type        | Type of action to create: <br />`Agents::EmailAgent`<br />`Agents::EventTransformationAgent`<br />`Agents::HTTPRequestAgent`<br />`Agents::IMAPAgent`<br />`Agents::LLMAgent`<br />`Agents::TriggerAgent`<br />`Agents::WebhookAgent`<br />`Agents::SendToStoryAgent`<br /> |
| agent_options     | JSON object with action options.                                                                                                                                                                                                                                            |
| folder_id         | **Optional** ID of the folder to place the template in. Set to `null` to remove from a folder. The folder must be a template folder belonging to the same team. Unchanged if omitted.                                                                                       |
| read_access       | **Optional** Control where this template can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`). Unchanged if omitted. (`SPECIFIC_TEAMS` is a premium feature. [Reach out to find out more](https://tines.com/contact-support).)                                                  |
| shared_team_slugs | **Optional** List of teams' slugs where this template can be used. Required when setting `read_access` to `SPECIFIC_TEAMS`. Unchanged if omitted.                                                                                                                           |
| action_inputs     | **Optional** Array of input parameters for the template. Replaces existing inputs when provided. Omit to keep existing inputs unchanged. See [action_inputs structure](#action-inputs-structure) below.                                                                     |

| Path Parameter | Description         |
| -------------- | ------------------- |
| template_id    | ID of the template. |

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/admin/templates/<<template_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'\
  -d '{
    "name": "Name+Update",
    "description": "test+Update",
    "agent_type": "Agents::EmailAgent",
    "vendor": "API+Update",
    "product": "API+Update",
    "agent_options": {
        "mode": "extract+Update",
        "matchers": [
            {
                "path": "{{.text}}",
                "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
                "to": "email_addresses"
            },
            {
                "path": "{{.text}}",
                "regexp": "https?:\\/\\/[\\S]+",
                "to": "urls"
            }
        ],
        "message": "This is an optional message+Update"
    },
    "read_access": "SPECIFIC_TEAMS",
    "shared_team_slugs": ["team-slug-1", "team-slug-2"],
    "action_inputs": [
        {
            "name": "input_text",
            "type": "TEXT",
            "description": "Updated input description",
            "required": true
        }
    ]
}'
```

## Response

A successful request will return the updated template with a `200` status.

### Field description

| Parameter         | Description                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| id                | Template ID.                                                                                                  |
| name              | Template name.                                                                                                |
| description       | Template description.                                                                                         |
| agent_type        | Type of action associated with the template.                                                                  |
| agent_options     | Action options.                                                                                               |
| vendor            | Vendor name.                                                                                                  |
| product           | Product name.                                                                                                 |
| team_id           | ID of team to which the template belongs.                                                                     |
| folder_id         | ID of the folder containing the template, or `null` if the template is not in a folder.                       |
| read_access       | Control where this template can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                 |
| shared_team_slugs | List of teams' slugs where this template can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty. |
| action_inputs     | **Optional** Array of input parameters for the template. See details below.                                   |

### action_inputs structure

Each object in the `action_inputs` array can have the following fields:

| Field         | Description                                                                                                     |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| name          | **Required** Input parameter name.                                                                              |
| type          | **Required** Input type. One of: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`, `OBJECT`, `OPTION`, `CODE`, `CREDENTIAL`. |
| required      | Whether the input is required. Defaults to `false`. Must be `true` for `CREDENTIAL` type.                       |
| description   | **Optional** Description of the input parameter.                                                                |
| default_value | **Optional** Default value for the input.                                                                       |
| data          | **Optional** Type-specific configuration object. See below.                                                     |

The `data` object varies by type:

_Note: The `data` field is only applicable to `OBJECT`, `OPTION` and `CODE` types. Other types do not use this field._

| Type   | data fields                                                               |
| ------ | ------------------------------------------------------------------------- |
| OBJECT | `json_schema`: JSON schema for validation.                                |
| OPTION | `options`: Array of option values. `multi_select_enabled`: Boolean.       |
| CODE   | `code_language`: Language for syntax highlighting (e.g., `HTML`, `JSON`). |

#### Type-specific default_value types

| Type       | Expected default_value type           |
| ---------- | ------------------------------------- |
| TEXT       | String                                |
| NUMBER     | Integer (or formula string)           |
| BOOLEAN    | Boolean (or formula string)           |
| DATE       | String                                |
| OBJECT     | Any valid JSON value                  |
| OPTION     | Array (or formula string)             |
| CODE       | String                                |
| CREDENTIAL | Not supported (field must be omitted) |

_Formula strings start with `=` (e.g., `=INPUT.name`) or `\\=` for escaped formulas._

#### CREDENTIAL type restrictions

`CREDENTIAL` inputs only accept `name`, `type`, and `required` fields. The `description`, `default_value`, and `data` fields are not permitted and will cause a validation error if included.

### Validation constraints

The following constraints are enforced when updating a template with `action_inputs`:

- **Maximum of 300** action inputs per template.
- **Only one** `CREDENTIAL` input is allowed per template.
- Input **names must be unique** across all action inputs.
- `CREDENTIAL` inputs must have `required` set to `true`.

### Sample response

```json
{
  "id": 472463921472463921,
  "name": "Name+Update",
  "description": "test+Update",
  "agent_type": "Agents::EmailAgent",
  "agent_options": {
    "mode": "extract+Update",
    "matchers": [
      {
        "path": "{{.text}}",
        "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
        "to": "email_addresses"
      },
      {
        "path": "{{.text}}",
        "regexp": "https?:\\/\\/[\\S]+",
        "to": "urls"
      }
    ],
    "message": "This is an optional message+Update"
  },
  "vendor": "API+Update",
  "product": "API+Update",
  "team_id": 1,
  "folder_id": 42,
  "read_access": "SPECIFIC_TEAMS",
  "shared_team_slugs": ["team-slug-1", "team-slug-2"],
  "action_inputs": [
    {
      "name": "input_text",
      "type": "TEXT",
      "description": "Updated input description",
      "required": true
    }
  ]
}
```

#### List

## Description

Retrieve a list of private templates. Optionally search to filter results.

## Request

HTTP Method: **GET**

| Parameter | Description                                                                                                              |
| --------- | ------------------------------------------------------------------------------------------------------------------------ |
| per_page  | **Optional** Set the number of results returned per page. Defaults to 20.                                                |
| page      | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1.                      |
| search    | **Optional** A search query to filter templates. Matches against name, description, vendor, product, and action options. |
| folder_id | **Optional** Filter templates by folder. Only returns templates in the specified folder.                                 |

### List all templates

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/templates \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

### Search templates

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/templates \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"search": "Test"}'
```

### List templates in a folder

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/templates?folder_id=<<folder_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array of private templates in the Tines tenant.

### Field description

| Parameter         | Description                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| id                | Template ID.                                                                                                  |
| name              | Template name.                                                                                                |
| description       | Template description.                                                                                         |
| agent_type        | Type of action associated with the template.                                                                  |
| agent_options     | Action options.                                                                                               |
| vendor            | Vendor name.                                                                                                  |
| product           | Product name.                                                                                                 |
| team_id           | ID of team to which the template belongs.                                                                     |
| folder_id         | ID of the folder containing the template, or `null` if the template is not in a folder.                       |
| read_access       | Control where this template can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                 |
| shared_team_slugs | List of teams' slugs where this template can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty. |
| action_inputs     | **Optional** Array of input parameters for the template. See details below.                                   |

### action_inputs structure

Each object in the `action_inputs` array can have the following fields:

| Field         | Description                                                                                                     |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| name          | **Required** Input parameter name.                                                                              |
| type          | **Required** Input type. One of: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`, `OBJECT`, `OPTION`, `CODE`, `CREDENTIAL`. |
| required      | Whether the input is required. Defaults to `false`. Must be `true` for `CREDENTIAL` type.                       |
| description   | **Optional** Description of the input parameter.                                                                |
| default_value | **Optional** Default value for the input.                                                                       |
| data          | **Optional** Type-specific configuration object. See below.                                                     |

The `data` object varies by type:

_Note: The `data` field is only applicable to `OBJECT`, `OPTION` and `CODE` types. Other types do not use this field._

| Type   | data fields                                                               |
| ------ | ------------------------------------------------------------------------- |
| OBJECT | `json_schema`: JSON schema for validation.                                |
| OPTION | `options`: Array of option values. `multi_select_enabled`: Boolean.       |
| CODE   | `code_language`: Language for syntax highlighting (e.g., `HTML`, `JSON`). |

### Sample response

```json
{
  "admin/templates": [
    {
      "id": "472463921472463921",
      "name": "Extract some text",
      "description": "test",
      "agent_type": "Agents::EventTransformationAgent",
      "agent_options": {
        "mode": "extract",
        "matchers": [
          {
            "path": "{{.text}}",
            "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
            "to": "email_addresses"
          },
          {
            "path": "{{.text}}",
            "regexp": "https?:\\/\\/[\\S]+",
            "to": "urls"
          }
        ],
        "message": "This is an optional message"
      },
      "vendor": "API",
      "product": "API",
      "team_id": 1,
      "folder_id": 42,
      "read_access": "TEAM",
      "shared_team_slugs": [],
      "action_inputs": [
        {
          "name": "input_text",
          "type": "TEXT",
          "description": "The text to extract from",
          "required": true,
          "default_value": "Sample text"
        },
        {
          "name": "output_format",
          "type": "OPTION",
          "description": "Output format selection",
          "required": false,
          "data": {
            "options": ["JSON", "CSV", "Plain Text"],
            "multi_select_enabled": false
          }
        }
      ]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/templates?page=1&per_page=20",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

#### Delete

## Description

Delete a private template by ID.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description         |
| -------------- | ------------------- |
| template_id    | ID of the template. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/templates/<<template_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Move

## Description

Move a private template to a different team.

> **Warning:**
>
> - Moving a template to a team will reset its access settings to team-only visibility.
> - Resources and credentials used in the template may not exist in the target team.

## Request

HTTP Method: **POST**

| Path Parameter | Description         |
| -------------- | ------------------- |
| template_id    | ID of the template. |

| Parameter | Description                                                                                                                                 |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id   | **Required** ID of the team to move the template to.                                                                                        |
| folder_id | **Optional** ID of a folder on the target team to place the template in. The folder must be a template folder belonging to the target team. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/admin/templates/<<template_id>>/move \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"team_id": "<<team_id>>"}'
```

## Response

A successful request will return the updated template.

### Field description

| Parameter         | Description                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| id                | Template ID.                                                                                                  |
| name              | Template name.                                                                                                |
| description       | Template description.                                                                                         |
| agent_type        | Type of action associated with the template.                                                                  |
| agent_options     | Action options.                                                                                               |
| vendor            | Vendor name.                                                                                                  |
| product           | Product name.                                                                                                 |
| team_id           | ID of team to which the template belongs.                                                                     |
| folder_id         | ID of the folder containing the template, or `null` if the template is not in a folder.                       |
| read_access       | Control where this template can be used (`TEAM`, `GLOBAL`, `SPECIFIC_TEAMS`).                                 |
| shared_team_slugs | List of teams' slugs where this template can be used when `read_access` is `SPECIFIC_TEAMS`, otherwise empty. |
| action_inputs     | **Optional** Array of input parameters for the template. See details below.                                   |

### action_inputs structure

Each object in the `action_inputs` array can have the following fields:

| Field         | Description                                                                                                     |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| name          | **Required** Input parameter name.                                                                              |
| type          | **Required** Input type. One of: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`, `OBJECT`, `OPTION`, `CODE`, `CREDENTIAL`. |
| required      | Whether the input is required. Defaults to `false`. Must be `true` for `CREDENTIAL` type.                       |
| description   | **Optional** Description of the input parameter.                                                                |
| default_value | **Optional** Default value for the input.                                                                       |
| data          | **Optional** Type-specific configuration object. See below.                                                     |

The `data` object varies by type:

_Note: The `data` field is only applicable to `OBJECT`, `OPTION` and `CODE` types. Other types do not use this field._

| Type   | data fields                                                               |
| ------ | ------------------------------------------------------------------------- |
| OBJECT | `json_schema`: JSON schema for validation.                                |
| OPTION | `options`: Array of option values. `multi_select_enabled`: Boolean.       |
| CODE   | `code_language`: Language for syntax highlighting (e.g., `HTML`, `JSON`). |

### Sample response

```json
{
  "id": 141,
  "name": "Extract some text",
  "description": "test",
  "agent_type": "Agents::EventTransformationAgent",
  "agent_options": {
    "mode": "extract",
    "matchers": [
      {
        "path": "",
        "regexp": "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b",
        "to": "email_addresses"
      },
      {
        "path": "",
        "regexp": "https?:\\/\\/[\\S]+",
        "to": "urls"
      }
    ],
    "message": "This is an optional message"
  },
  "vendor": "API",
  "product": "API",
  "team_id": 2,
  "folder_id": null,
  "read_access": "TEAM",
  "shared_team_slugs": [],
  "action_inputs": [
    {
      "name": "input_text",
      "type": "TEXT",
      "description": "The text to extract from",
      "required": true,
      "default_value": "Sample text"
    }
  ]
}
```

### Tunnels

#### Health

## Description

Retrieve the health status of tunnels. Note: A user must be an admin in order to access this endpoint i.e. a tenant-level API key is required.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| tunnel_name     | **Required** Include the name of your tunnel                                                        |
| per_page        | **Optional** Set the number of results returned per page.                                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/tunnel/<<tunnel_name>>/health \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

Here `tunnel_name` is the name of your tunnel

## Response

A successful request will return a JSON object describing the health of the Tunnel. There is more information about health statuses in our Tunnel FAQ page here : https://www.tines.com/docs/admin/tunnel/tunnel-faqs/

### Field description

| Parameter | Description                                                                  |
| --------- | ---------------------------------------------------------------------------- |
| health    | The health of the tunnel. Accepted values: Down, Healthy, Inactive, Degraded |
| name      | The team name.                                                               |

### Sample response

<!-- cspell:disable -->

```json
{
  "health": "Healthy"
}
```

<!-- cspell:enable -->

### Users

#### Create

## Description

Create a user in a Tines tenant.

## Request

HTTP Method: **POST**

| Query Parameter    | Description                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| email              | User email.                                                                                                                                                        |
| first_name         | **Optional** User first name.                                                                                                                                      |
| last_name          | **Optional** User last name.                                                                                                                                       |
| admin              | **Optional** Boolean flag indicating whether user should be admin. Default is false.                                                                               |
| tenant_permissions | **Optional** Array of tenant permission names, from the [list of available permissions](https://www.tines.com/docs/admin/user-administration/#tenant-permissions). |
| is_active          | **Optional** Boolean flag indicating whether user is active. Default is true.                                                                                      |

### Sample request

```bash

curl -X POST \
  https://<tenant-domain>/api/v1/admin/users \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
       "email": "alice@tines.xyz",
       "first_name": "Alice",
       "last_name": "Smith",
       "admin": false,
       "tenant_permissions": ["AUDIT_LOG_READ"]
      }'
```

## Response

A successful request will return a JSON object describing the created user.

### Field description

| Parameter          | Description                                                       |
| ------------------ | ----------------------------------------------------------------- |
| id                 | User ID.                                                          |
| scim_uid           | SCIM User ID.                                                     |
| email              | User email.                                                       |
| admin              | Boolean flag indicating whether user is an admin.                 |
| tenant_permissions | Array of tenant permission names.                                 |
| is_active          | Boolean flag indicating whether user is active.                   |
| created_at         | Timestamp describing when the user was created.                   |
| updated_at         | Timestamp describing when the user was last updated.              |
| first_name         | User first name.                                                  |
| last_name          | User last name.                                                   |
| last_seen          | Timestamp describing when the user was last seen in the platform. |

### Sample response

```json
{
  "id": 276,
  "scim_uid": null,
  "email": "alice@tines.xyz",
  "created_at": "2019-11-03T09:57:49.537Z",
  "updated_at": "2019-11-03T09:57:49.537Z",
  "admin": false,
  "is_active": true,
  "first_name": "Alice",
  "last_name": "Smith",
  "last_seen": null,
  "tenant_permissions": ["AUDIT_LOG_READ"]
}
```

#### Get

## Description

Retrieve details of a specific user.

## Request

HTTP Method: **GET**

| Path Parameter | Description     |
| -------------- | --------------- |
| user_id        | ID of the user. |

| Query Parameter | Description                              |
| --------------- | ---------------------------------------- |
| include         | **Optional** Include: `team_memberships` |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/users/<<user_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

Or, to include `team_memberships`:

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/users/<<user_id>>?include=team_memberships \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the User.

### Field description

| Parameter          | Description                                                       |
| ------------------ | ----------------------------------------------------------------- |
| id                 | User ID.                                                          |
| scim_uid           | SCIM User ID.                                                     |
| email              | User email.                                                       |
| admin              | Boolean flag indicating whether user is an admin.                 |
| tenant_permissions | Array of tenant permission names.                                 |
| is_active          | Boolean flag indicating whether user is active.                   |
| created_at         | Timestamp describing when the user was created.                   |
| updated_at         | Timestamp describing when the user was last updated.              |
| first_name         | User first name.                                                  |
| last_name          | User last name.                                                   |
| last_seen          | Timestamp describing when the user was last seen in the platform. |

If `?include=team_memberships` is included, a successful response will include a JSON array of the following:

| Parameter | Description               |
| --------- | ------------------------- |
| team_id   | Team ID.                  |
| team_name | Team name.                |
| role      | User's role in this team. |

### Sample response

```json
{
  "id": 2,
  "scim_uid": "123",
  "email": "alice@tines.xyz",
  "created_at": "2019-07-08T14:16:11.967Z",
  "updated_at": "2019-10-31T21:40:52.682Z",
  "admin": false,
  "is_active": true,
  "first_name": "Alice",
  "last_name": "Smith",
  "last_seen": "2019-10-31T21:40:42.000Z",
  "tenant_permissions": ["AUDIT_LOG_READ"]
}
```

Or, if `?include=team_memberships` is included in the request:

```json
{
  ...,
  "team_memberships": [
    { "team_id": 7, "team_name": "Security Ops", "role": "EDITOR" },
    { "team_id": 12, "team_name": "IR Cases", "role": "CASE_MANAGER" }
  ]
}
```

#### Get sign-in activities

## Description

Retrieve a list of sign-in activities by a specified user. This endpoint returns a subset of the data available at the [Audit Logs endpoint](https://www.tines.com/api/audit-logs/list).

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| before          | **Optional** Only include logins created before this timestamp                                      |
| after           | **Optional** Only include logins created after this timestamp                                       |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |

| Path Parameter | Description     |
| -------------- | --------------- |
| user_id        | ID of the user. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/users/<<user_id>>/signin_activities \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array describing login events by the specified user.

### Field description

| Parameter  | Description                                 |
| ---------- | ------------------------------------------- |
| id         | Signin Activity ID.                         |
| user_id    | Id of user that signed in                   |
| user_agent | User agent string of client used to sign in |
| ip         | IP address of signin                        |
| created_at | Timestamp describing when user logged in    |

### Sample response

<!-- cspell:disable -->

```json
{
  "admin/login_activities": [
    {
      "created_at": "2022-10-19T14:25:18Z",
      "id": 220,
      "user_id": 72,
      "ip": "192.158.1.38",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
    },
    {
      "created_at": "2022-10-19T14:25:18Z",
      "id": 219,
      "user_id": 72,
      "ip": "2001:db8:3333:4444:5555:6666:7777:8888",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/users/72/signin_activities?per_page=20&page=1",
    "previous_page": null,
    "next_page": "https://<tenant-domain>/api/v1/admin/users/72/signin_activities?per_page=20&page=2",
    "next_page_number": 2,
    "per_page": 20,
    "pages": 2,
    "count": 25
  }
}
```

<!-- cspell:enable -->

#### Update

## Description

Update a User.

## Request

HTTP Method: **PUT**

| Parameter          | Description                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| email              | **Optional** User email.                                                                                                                                           |
| first_name         | **Optional** User first name.                                                                                                                                      |
| last_name          | **Optional** User last name.                                                                                                                                       |
| admin              | **Optional** Boolean flag indicating whether user should be admin. Default is false.                                                                               |
| tenant_permissions | **Optional** Array of tenant permission names, from the [list of available permissions](https://www.tines.com/docs/admin/user-administration/#tenant-permissions). |
| is_active          | **Optional** Boolean flag indicating whether user should be active. Default is true.                                                                               |

| Path Parameter | Description     |
| -------------- | --------------- |
| user_id        | ID of the user. |

### Sample request

<!-- cspell:disable -->

```bash

curl -X PUT \
  https://<tenant-domain>/api/v1/admin/users/<<user_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
  -d '{
       "admin": true,
       "last_name": "Bloggs"
      }'
```

<!-- cspell:enable -->

## Response

A successful request will return a JSON object describing the updated user.

### Field description

| Parameter          | Description                                                       |
| ------------------ | ----------------------------------------------------------------- |
| id                 | User ID.                                                          |
| scim_uid           | SCIM User ID.                                                     |
| email              | User email.                                                       |
| admin              | Boolean flag indicating whether user is an admin.                 |
| tenant_permissions | Array of tenant permission names.                                 |
| is_active          | Boolean flag indicating whether user is active.                   |
| created_at         | Timestamp describing when the user was created.                   |
| updated_at         | Timestamp describing when the user was last updated.              |
| first_name         | User first name.                                                  |
| last_name          | User last name.                                                   |
| last_seen          | Timestamp describing when the user was last seen in the platform. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 3,
  "scim_uid": null,
  "email": "alice@tines.xyz",
  "created_at": "2019-11-03T09:57:49.537Z",
  "updated_at": "2019-11-03T09:57:49.537Z",
  "admin": true,
  "is_active": true,
  "first_name": "Alice",
  "last_name": "Bloggs",
  "last_seen": null,
  "tenant_permissions": null
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of users from the Tines tenant.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20.                           |
| page            | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. |
| filter          | **Optional** Filter: `TENANT_OWNER`                                                                 |
| include         | **Optional** Include: `team_memberships`                                                            |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/admin/users \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON Array of users in the Tines tenant.

### Field description

| Parameter          | Description                                                       |
| ------------------ | ----------------------------------------------------------------- |
| id                 | User ID.                                                          |
| scim_uid           | SCIM User ID.                                                     |
| email              | User email.                                                       |
| admin              | Boolean flag indicating whether user is an admin.                 |
| tenant_permissions | Array of tenant permission names.                                 |
| is_active          | Boolean flag indicating whether user is active.                   |
| created_at         | Timestamp describing when the user was created.                   |
| updated_at         | Timestamp describing when the user was last updated.              |
| first_name         | User first name.                                                  |
| last_name          | User last name.                                                   |
| last_seen          | Timestamp describing when the user was last seen in the platform. |

If `?include=team_memberships` is included, a successful response will include the following JSON array for each User:

| Parameter | Description               |
| --------- | ------------------------- |
| team_id   | Team ID.                  |
| team_name | Team name.                |
| role      | User's role in this team. |

### Sample response

<!-- cspell:disable -->

```json
{
  "admin/users": [
    {
      "id": 2,
      "scim_uid": null,
      "email": "alice@tines.xyz",
      "created_at": "2019-07-08T14:16:11.967Z",
      "updated_at": "2019-10-31T21:40:52.682Z",
      "admin": false,
      "is_active": true,
      "first_name": "Alice",
      "last_name": "Smith",
      "last_seen": "2019-10-31T21:40:42.000Z",
      "tenant_permissions": ["AUDIT_LOG_READ"]
    },
    {
      "id": 3,
      "scim_uid": null,
      "email": "joe@tines.xyz",
      "created_at": "2019-09-11T14:46:37.880Z",
      "updated_at": "2019-09-27T20:45:46.527Z",
      "admin": false,
      "is_active": true,
      "first_name": "Joe",
      "last_name": "Bloggs",
      "last_seen": "2019-09-27T20:45:46.000Z",
      "tenant_permissions": []
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/users?page=1&per_page=20",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

Or, if `?include=team_memberships` is included in the request:

```json
{
  "admin/users": [
    {
      ...,
      "team_memberships": [
        { "team_id": 7, "team_name": "Security Ops", "role": "EDITOR" },
        { "team_id": 12, "team_name": "IR Cases", "role": "CASE_MANAGER" }
      ]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/admin/users?page=1&per_page=20",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a specific user.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description     |
| -------------- | --------------- |
| user_id        | ID of the user. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/admin/users/<<user_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

#### Resend invitation

## Description

Resend platform invitation to specified user.

## Request

HTTP Method: **POST**

| Path Parameter | Description                          |
| -------------- | ------------------------------------ |
| user_id        | ID of the user to resend invitation. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/admin/users/<<user_id>>/resend_invitation \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return either an empty response or a message stating `User already accepted invitation.` both with a `200` status code.

### Sample response

```json
{
  "message": "User already accepted invitation."
}
```

#### Expire session

## Description

Expires a user’s session, signing them out of the Tines tenant on all devices.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description     |
| -------------- | --------------- |
| user_id        | ID of the user. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/admin/users/<<user_id>>/expire_session \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

## Dashboards

### List

## Description

Retrieve a paginated list of dashboards.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                                                                 |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** Only return dashboards belonging to this team.                                                                                 |
| order           | **Optional** Order the results by one of: `NAME`, `RECENTLY_CREATED`, `RECENTLY_UPDATED`. Defaults to `NAME` when not specified or invalid. |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 500.                                                   |
| page            | **Optional** The page number of results to fetch. Defaults to page 1.                                                                       |

```bash
curl -X GET \
  "https://<tenant-domain>/api/v1/dashboards?team_id=1&order=RECENTLY_UPDATED" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the dashboards the requesting token has access to.

### Field description

| Parameter  | Description                                                          |
| ---------- | -------------------------------------------------------------------- |
| id         | The ID of the dashboard.                                             |
| name       | Name of the dashboard.                                               |
| team       | Object describing the team this dashboard belongs to.                |
| team.id    | Integer ID of the team.                                              |
| team.name  | Name of the team.                                                    |
| created_at | ISO 8601 timestamp representing when the dashboard was created.      |
| updated_at | ISO 8601 timestamp representing when the dashboard was last updated. |

### Sample response

<!-- cspell:disable -->

```json
{
  "dashboards": [
    {
      "id": 1,
      "name": "Example dashboard",
      "team": {
        "id": 2,
        "name": "Audits"
      },
      "created_at": "2026-02-18T19:38:13Z",
      "updated_at": "2026-02-18T21:53:21Z"
    },
    {
      "id": 2,
      "name": "Imported dashboard",
      "team": {
        "id": 3,
        "name": "Triage"
      },
      "created_at": "2026-03-10T17:47:52Z",
      "updated_at": "2026-03-10T17:47:52Z"
    }
  ],
  "meta": {
    "current_page": "http://<tenant-domain>/api/v1/dashboards?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Delete

## Description

Delete a dashboard.

## Request

HTTP Method: **DELETE**

| Parameter    | Description                        |
| ------------ | ---------------------------------- |
| dashboard_id | The ID of the dashboard to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/dashboards/<<dashboard_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return an empty response with a `204` status code.

### Export

## Description

Export a dashboard. The response can be passed to the import endpoint to recreate the dashboard in another team or tenant.

## Request

HTTP Method: **GET**

| Path Parameter | Description                        |
| -------------- | ---------------------------------- |
| id             | The ID of the dashboard to export. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/dashboards/<<id>>/export \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the dashboard in exported form.

### Field description

| Parameter      | Description                                                    |
| -------------- | -------------------------------------------------------------- |
| schema_version | The version of the export schema format.                       |
| export_type    | The type of the export, "dashboard".                           |
| exported_at    | ISO 8601 timestamp representing when the export was generated. |
| dashboard      | Object containing the dashboard configuration.                 |

### Sample response

<!-- cspell:disable -->

```json
{
  "schema_version": 1,
  "export_type": "dashboard",
  "exported_at": "2025-02-10T12:30:45Z",
  "dashboard": {
    "name": "Security overview",
    "notes": [
      {
        "id": "a1b2c3d4e5f6",
        "name": "Welcome note",
        "content": "This dashboard shows key security metrics."
      }
    ],
    "case_lists": [
      {
        "id": "b2c3d4e5f6a1",
        "name": "Open cases",
        "type": "cases",
        "color_palette": "default",
        "filters": {},
        "options": {}
      }
    ],
    "record_charts": [
      {
        "id": "c3d4e5f6a1b2",
        "name": "Priority breakdown",
        "record_type_name": "Cases",
        "element_type": "chart",
        "axes": [
          {
            "direction": "x",
            "record_field_name": "priority"
          }
        ],
        "aggregation": "COUNT",
        "rolling_date_range": null,
        "range_start": null,
        "range_end": null,
        "filters": {
          "stories": [],
          "fields": [
            {
              "value": "open",
              "operator": "equals",
              "name": "status"
            }
          ]
        },
        "order": {
          "field": null,
          "direction": null
        },
        "color_palette": "default",
        "options": {},
        "record_fields": [],
        "test_mode": false
      }
    ],
    "layout": [
      {
        "i": "a1b2c3d4e5f6",
        "x": 0,
        "y": 0,
        "w": 12,
        "h": 2
      },
      {
        "i": "b2c3d4e5f6a1",
        "x": 0,
        "y": 2,
        "w": 6,
        "h": 4
      },
      {
        "i": "c3d4e5f6a1b2",
        "x": 6,
        "y": 2,
        "w": 6,
        "h": 4
      }
    ]
  }
}
```

<!-- cspell:enable -->

### Import

## Description

Import a dashboard. The import data should be obtained from the [export endpoint](/api/dashboards/export).

## Request

HTTP Method: **POST**

| Body Parameter | Description                                                                                                                                          |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id        | ID of the team to which the dashboard should be added.                                                                                               |
| data           | JSON object representing the dashboard in [exported form](/api/dashboards/export#sample-response).                                                   |
| mode           | **Optional** Create a new dashboard or replace an existing one by name - (`new`, `replace`). Defaults to `new`.                                      |
| new_name       | **Optional** Name for the dashboard. Defaults to the name in the export data. In `replace` mode, this specifies which existing dashboard to replace. |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/dashboards/import \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "team_id": 14,
    "new_name": "Imported dashboard",
    "mode": "new",
    "data": {
      "schema_version": 1,
      "dashboard": {
        "name": "Security overview",
        "notes": [
          {
            "id": "a1b2c3d4e5f6",
            "name": "Welcome note",
            "content": "This dashboard shows key security metrics."
          }
        ],
        "case_lists": [
          {
            "id": "b2c3d4e5f6a1",
            "name": "Open cases",
            "type": "cases",
            "color_palette": "default",
            "filters": {},
            "options": {}
          }
        ],
        "record_charts": [
          {
            "id": "c3d4e5f6a1b2",
            "name": "Priority breakdown",
            "record_type_name": "Cases",
            "element_type": "chart",
            "axes": [
              {
                "direction": "x",
                "record_field_name": "priority"
              }
            ],
            "aggregation": "COUNT",
            "rolling_date_range": null,
            "range_start": null,
            "range_end": null,
            "filters": {
              "stories": [],
              "fields": [
                {
                  "value": "open",
                  "operator": "equals",
                  "name": "status"
                }
              ]
            },
            "order": {
              "field": null,
              "direction": null
            },
            "color_palette": "default",
            "options": {},
            "record_fields": [],
            "test_mode": false
          }
        ],
        "layout": [
          {
            "i": "a1b2c3d4e5f6",
            "x": 0,
            "y": 0,
            "w": 12,
            "h": 2
          },
          {
            "i": "b2c3d4e5f6a1",
            "x": 0,
            "y": 2,
            "w": 6,
            "h": 4
          },
          {
            "i": "c3d4e5f6a1b2",
            "x": 6,
            "y": 2,
            "w": 6,
            "h": 4
          }
        ]
      }
    }
  }'
```

## Response

A successful request will return a JSON object describing the newly created dashboard with a `201` status code.

### Field description

| Parameter  | Description                                                          |
| ---------- | -------------------------------------------------------------------- |
| id         | The ID of the dashboard.                                             |
| name       | Name of the dashboard.                                               |
| team       | Object describing the team this dashboard belongs to.                |
| team.id    | Integer ID of the team.                                              |
| team.name  | Name of the team.                                                    |
| created_at | ISO 8601 timestamp representing when the dashboard was created.      |
| updated_at | ISO 8601 timestamp representing when the dashboard was last updated. |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 42,
  "name": "Imported dashboard",
  "team": {
    "id": 14,
    "name": "Security Operations"
  },
  "created_at": "2025-02-14T10:30:00Z",
  "updated_at": "2025-02-14T10:30:00Z"
}
```

<!-- cspell:enable -->

## Info

### GET

## Description

Retrieve basic information regarding your tenant's stack.

## Request

HTTP Method: **GET**

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/info \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the information related to your tenant's stack.

### Field description

| Parameter  | Description                                                                |
| ---------- | -------------------------------------------------------------------------- |
| stack      | An object containing the stack information.                                |
| name       | The name assigned to the tenant's stack.                                   |
| type       | Whether the stack is dedicated or shared.                                  |
| region     | The AWS region that the stack is hosted in.                                |
| egress_ips | The origin IPs that are used for any request originating from your tenant. |

### Sample response

<!-- cspell:disable -->

```json
{
  "stack": {
    "name": "stackName",
    "type": "shared",
    "region": "eu-west-1",
    "egress_ips": ["192.168.0.1", "192.168.0.2"]
  }
}
```

<!-- cspell:enable -->

### GET Web Statistics

## Description

Retrieve operational information about your web server.

If you are running multiple tines-web containers then this will only reflect the stats for the container that handled this request. If you are using a load balancer this would be an arbitrary container unless you made the request from within the container's network.

**Note:** This endpoint is accessible only to self-hosted customers.

## Request

HTTP Method: **GET**

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/info/web_stats \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing information about Puma web server status.

See https://puma.io/puma/file.stats.html for details.

### Sample Response

```json
{
  "started_at": "2025-03-06T10:10:14Z",
  "backlog": 0,
  "running": 16,
  "pool_capacity": 15,
  "busy_threads": 1,
  "max_threads": 16,
  "requests_count": 2057,
  "versions": {
    "puma": "6.6.0",
    "ruby": { "engine": "ruby", "version": "3.4.2", "patchlevel": 28 }
  }
}
```

### GET Worker Statistics

## Description

Retrieve essential information about worker statistics.

**Note:** This endpoint is accessible only to self-hosted customers.

## Request

HTTP Method: **GET**

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/info/worker_stats \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing the information related to worker statistics.

### Field description

| Parameter       | Description                            |
| --------------- | -------------------------------------- |
| current_workers | The number of active workers.          |
| max_workers     | The maximum number of workers allowed. |
| queue_count     | The total count of items in the queue. |
| queue_latency   | The latency of the default queue.      |

### Sample Response

```json
{
  "current_workers": 10,
  "max_workers": 20,
  "queue_count": 100,
  "queue_latency": 500
}
```

## SCIM

## About SCIM

Tines provides [SCIM v2.0](https://simplecloud.info/)-compliant endpoints to enable synchronization with Identity Providers (IdPs) that support this standard.

## Configuration

These endpoints are only available for tenants with plans that include the SCIM feature and have enabled SCIM (in the tenant's Authentication settings).

## Endpoints

The base endpoint for SCIM operations (this is what needs to be configured in most IdPs) is `https://<tenant-domain>/api/scim/v2`

All endpoints expect the `application/scim+json` Content type.

| Verb     | Path                                 | Description                                                                                           |
| -------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| `GET`    | `/api/scim/v2/ServiceProviderConfig` | Show Configurations                                                                                   |
| `GET`    | `/api/scim/v2/ResourceTypes`         | Index Resource Types                                                                                  |
| `GET`    | `/api/scim/v2/ResourceTypes/:name `  | Show Resource Type                                                                                    |
| `GET`    | `/api/scim/v2/Schemas`               | Index Schemas                                                                                         |
| `GET`    | `/api/scim/v2/Users`                 | Index Users                                                                                           |
| `GET`    | `/api/scim/v2/Users/:id`             | Show User                                                                                             |
| `POST`   | `/api/scim/v2/Users`                 | Create User                                                                                           |
| `PUT`    | `/api/scim/v2/Users/:id`             | Replace User                                                                                          |
| `PATCH`  | `/api/scim/v2/Users/:id`             | Update User                                                                                           |
| `DELETE` | `/api/scim/v2/Users/:id`             | Disable User. Use the UI or [this API endpoint](/api/admin/users/delete) to completely delete a user. |
| `GET`    | `/api/scim/v2/Groups`                | Index Groups                                                                                          |
| `GET`    | `/api/scim/v2/Groups/:id`            | Show Group                                                                                            |
| `POST`   | `/api/scim/v2/Groups`                | Create Group                                                                                          |
| `PUT`    | `/api/scim/v2/Groups/:id`            | Replace Group                                                                                         |
| `PATCH`  | `/api/scim/v2/Groups/:id`            | Update Group                                                                                          |
| `DELETE` | `/api/scim/v2/Groups/:id`            | Destroy Group                                                                                         |

## Authentication

Authentication for all endpoints follows the same scheme as the rest of the API (described in the Authentication section). Only API keys with tenant owner access can be used for SCIM.

## Supported attributes

### Users

| name              | Type          | Description                                                                                                           |
| ----------------- | ------------- | --------------------------------------------------------------------------------------------------------------------- |
| `userName`        | `string`      | The username for the user. It must be the user's email.                                                               |
| `name.givenName`  | `string`      | The first name of the user.                                                                                           |
| `name.familyName` | `string`      | The last name of the user.                                                                                            |
| `externalId`      | `string`      | This identifier is generated by the IdP provider.                                                                     |
| `id`              | `string`      | Identifier generated by Tines.                                                                                        |
| `active`          | `boolean`     | Whether the identity is active (true) or not (false).                                                                 |
| `userType`        | `string`      | `"TENANT_OWNER"` (also known as `admin: true` in the Tines API User schema) or `"USER"`.                              |
| `groups`          | `ComplexType` | Array of groups this user belongs to. This field is immutable and must be changed by updating each Group's `members`. |

### Groups

| name          | Type          | Description                                          |
| ------------- | ------------- | ---------------------------------------------------- |
| `displayName` | `string`      | The display name for the group.                      |
| `externalId`  | `string`      | This identifier is generated by the IdP provider.    |
| `id`          | `string`      | Identifier generated by Tines.                       |
| `members`     | `ComplexType` | Array of Users and Groups that belong to this Group. |

## Tags

### Create

## Description

Create a tag in Tines.

## Request

HTTP Method: **POST**

| Body Parameter | Description                                                                                                            |
| -------------- | ---------------------------------------------------------------------------------------------------------------------- |
| name           | Tag name.                                                                                                              |
| team_id        | ID of the team to which the tag belongs.                                                                               |
| color          | Tag color. Use one of `purple`, `blue`, `gold`, `green`, `magenta`, `red`, `orange`, `mint`, or a `#RRGGBB` hex value. |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/tags \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "priority-high",
        "team_id": 1,
        "color": "red"
      }'
```

## Response

A successful request will return a JSON object describing the created tag.

### Field description

| Parameter  | Description                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------ |
| id         | Tag ID.                                                                                          |
| name       | Tag name.                                                                                        |
| color      | Tag color in hex format (for example, `#FF8888`).                                                |
| teams      | The teams the tag belongs to. Tags created after February 2025 can only belong to a single team. |
| created_at | ISO 8601 timestamp describing when the tag was created.                                          |
| updated_at | ISO 8601 timestamp describing when the tag was last updated.                                     |

### Sample response

```json
{
  "id": 17,
  "name": "priority-high",
  "color": "#FF8888",
  "teams": [
    {
      "id": 276,
      "name": "Test team 1"
    }
  ],
  "created_at": "2026-04-10T09:15:42.128Z",
  "updated_at": "2026-04-10T09:15:42.128Z"
}
```

### Get

## Description

Retrieve a single tag.

## Request

HTTP Method: **GET**

| Path Parameter | Description                    |
| -------------- | ------------------------------ |
| tag_id         | The ID of the tag to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/tags/<<tag_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the requested tag.

### Field description

| Parameter  | Description                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------ |
| id         | Tag ID.                                                                                          |
| name       | Tag name.                                                                                        |
| color      | Tag color in hex format (for example, `#8D75E6`).                                                |
| teams      | The teams the tag belongs to. Tags created after February 2025 can only belong to a single team. |
| created_at | ISO 8601 timestamp describing when the tag was created.                                          |
| updated_at | ISO 8601 timestamp describing when the tag was last updated.                                     |

### Sample response

```json
{
  "id": 17,
  "name": "priority-high",
  "color": "#FF8888",
  "teams": [
    {
      "id": 276,
      "name": "Test team 1"
    }
  ],
  "created_at": "2026-04-10T09:15:42.128Z",
  "updated_at": "2026-04-10T09:15:42.128Z"
}
```

### Update

## Description

Update a tag.

## Request

HTTP Method: **PUT**

| Path Parameter | Description                  |
| -------------- | ---------------------------- |
| tag_id         | The ID of the tag to update. |

| Body Parameter | Description                                                                                                                                 |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| name           | **Optional** Updated tag name.                                                                                                              |
| color          | **Optional** Updated tag color. Use one of `purple`, `blue`, `gold`, `green`, `magenta`, `red`, `orange`, `mint`, or a `#RRGGBB` hex value. |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/tags/<<tag_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "priority-critical",
        "color": "#9B61FF"
      }'
```

## Response

A successful request will return a JSON object describing the updated tag.

### Field description

| Parameter  | Description                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------ |
| id         | Tag ID.                                                                                          |
| name       | Tag name.                                                                                        |
| color      | Tag color in hex format (for example, `#9B61FF`).                                                |
| teams      | The teams the tag belongs to. Tags created after February 2025 can only belong to a single team. |
| created_at | ISO 8601 timestamp describing when the tag was created.                                          |
| updated_at | ISO 8601 timestamp describing when the tag was last updated.                                     |

### Sample response

```json
{
  "id": 17,
  "name": "priority-critical",
  "color": "#9B61FF",
  "teams": [
    {
      "id": 276,
      "name": "Test team 1"
    }
  ],
  "created_at": "2026-04-10T09:15:42.128Z",
  "updated_at": "2026-04-10T11:04:03.772Z"
}
```

### List

## Description

Retrieve a list of tags.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                           |
| --------------- | --------------------------------------------------------------------- |
| team_id         | **Optional** Return tags belonging to this team.                      |
| per_page        | **Optional** The number of results returned per page. Defaults to 20. |
| page            | **Optional** The page of results to return. Defaults to 1.            |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/tags \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing an array of tags the requesting token has access to.

### Field description

| Parameter  | Description                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------ |
| id         | Tag ID.                                                                                          |
| name       | Tag name.                                                                                        |
| color      | Tag color in hex format (for example, `#8D75E6`).                                                |
| teams      | The teams the tag belongs to. Tags created after February 2025 can only belong to a single team. |
| created_at | ISO 8601 timestamp describing when the tag was created.                                          |
| updated_at | ISO 8601 timestamp describing when the tag was last updated.                                     |

### Sample response

```json
{
  "tags": [
    {
      "id": 17,
      "name": "priority-high",
      "color": "#FF8888",
      "teams": [
        {
          "id": 100,
          "name": "Test team 1"
        },
        {
          "id": 101,
          "name": "Test team 2"
        }
      ],
      "created_at": "2026-04-10T09:15:42.128Z",
      "updated_at": "2026-04-10T09:15:42.128Z"
    },
    {
      "id": 18,
      "name": "triage",
      "color": "#4BBFFF",
      "teams": [
        {
          "id": 276,
          "name": "Test team 3"
        }
      ],
      "created_at": "2026-04-10T09:21:11.491Z",
      "updated_at": "2026-04-10T09:21:11.491Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/tags?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 2
  }
}
```

### Delete

## Description

Delete a tag. If a `team_id` is provided, the tag will only be deleted for that team.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description                  |
| -------------- | ---------------------------- |
| tag_id         | The ID of the tag to delete. |

| Query Parameter | Description                                         |
| --------------- | --------------------------------------------------- |
| team_id         | **Optional** ID of the team to remove the tag from. |

### Sample request

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/tags/<<tag_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"team_id": 5}'
```

## Response

A successful request will return an empty response with a `204` status code.

## Workbench

### Get

## Description

Retrieve a workbench conversation.

## Request

HTTP Method: **GET**

| Parameter | Description                               |
| --------- | ----------------------------------------- |
| guid      | The GUID of the conversation to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/workbench/<<guid>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object representing the specified workbench conversation.

### Field description

| Parameter | Description                                                                         |
| --------- | ----------------------------------------------------------------------------------- |
| guid      | The conversation GUID.                                                              |
| steps     | An array of conversation messages including user, assistant, and tool interactions. |

### Sample response

```json
{
  "guid": "ca68403e-5594-42a4-bec7-879b0b417a83",
  "steps": [
    {
      "role": "user",
      "contents": [
        {
          "text": "what is the weather in dublin",
          "type": "text"
        }
      ]
    },
    {
      "role": "assistant",
      "contents": [
        {
          "type": "text",
          "text": "I'll check the current weather in Dublin for you."
        }
      ]
    },
    {
      "role": "tool",
      "name": "simple_weather_api_get_weather_at_location",
      "inputs": {
        "location": "Dublin"
      },
      "output": {
        "body": {
          "dt": 1750408079,
          "uvi": 2.32,
          "temp": 294.61,
          "clouds": 75,
          "sunset": 1750453002,
          "sunrise": 1750391791,
          "weather": [
            {
              "id": 803,
              "icon": "04d",
              "main": "Clouds",
              "description": "broken clouds"
            }
          ],
          "humidity": 63,
          "location": "Dublin",
          "pressure": 1020,
          "wind_deg": 140,
          "dew_point": 287.28,
          "feels_like": 294.46,
          "visibility": 10000,
          "wind_speed": 4.12
        },
        "meta": {
          "response_time": 2.341445
        },
        "status": 200,
        "headers": {
          "date": "Fri, 20 Jun 2025 08:28:00 GMT",
          "connection": "keep-alive",
          "content-type": "application/json; charset=utf-8",
          "x-request-id": "27f3829f-b8d3-462b-926d-887922459c36",
          "cache-control": "max-age=0, private, must-revalidate",
          "content-length": "318",
          "x-tines-status": "data_received",
          "referrer-policy": "strict-origin-when-cross-origin",
          "x-frame-options": "SAMEORIGIN",
          "x-tines-event-id": "198402032",
          "x-tines-revision": "47076c83",
          "x-xss-protection": "0",
          "x-download-options": "noopen",
          "content-disposition": "inline",
          "x-content-type-options": "nosniff",
          "x-tines-exit-action-id": "145208",
          "content-security-policy": "default-src 'none'",
          "x-tines-response-location": "https://v1.simple-weather-api.com/api/public/weather/result/yFb6cLyLoY0SZRpevrYCQg==",
          "x-permitted-cross-domain-policies": "none"
        }
      }
    },
    {
      "role": "assistant",
      "contents": [
        {
          "type": "text",
          "text": "Currently in Dublin, it's 21.46°C (70.63°F) with broken clouds. The humidity is at 63% with a wind speed of 4.12 m/s from the southeast (140°). Visibility is good at 10 km, and the air pressure is 1020 hPa. The sun rose at 5:43 AM and will set at 9:50 PM local time."
        }
      ]
    },
    {
      "role": "user",
      "contents": [
        {
          "text": "thanks workbench",
          "type": "text"
        }
      ]
    },
    {
      "role": "assistant",
      "contents": [
        {
          "type": "text",
          "text": "You're welcome! Let me know if you need any other information."
        }
      ]
    }
  ]
}
```

### List

## Description

List workbench conversations.

## Request

HTTP Method: **GET**

| Parameter  | Description                                                                                         | Required | Type    |
| ---------- | --------------------------------------------------------------------------------------------------- | -------- | ------- |
| search     | **Optional** Search term to filter conversations by title                                           | No       | String  |
| favorited  | **Optional** Filter by favorited status (true/false)                                                | No       | Boolean |
| creator_id | **Optional** Filter conversations by the ID of the creator (user)                                   | No       | String  |
| per_page   | **Optional** Set the number of results returned per page                                            | No       | Integer |
| page       | **Optional** Specify the page of results to return if there are multiple pages. Defaults to page 1. | No       | Integer |

```bash
curl -X GET \
  "https://<tenant-domain>/api/v1/workbench?per_page=20&page=1&search=weather&favorited=true" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object containing a paginated list of workbench conversations.

### Field description

| Parameter     | Description                                               |
| ------------- | --------------------------------------------------------- |
| conversations | Array of Workbench conversation objects                   |
| guid          | The conversation GUID                                     |
| title         | The conversation title                                    |
| source        | The source of the conversation                            |
| favorited     | Whether the conversation is favorited by the user         |
| creator_id    | The ID of the user who created the conversation           |
| created_at    | ISO 8601 timestamp when the conversation was created      |
| updated_at    | ISO 8601 timestamp when the conversation was last updated |

### Sample response

```json
{
  "conversations": [
    {
      "guid": "ca68403e-5594-42a4-bec7-879b0b417a83",
      "title": "Reset user password",
      "source": "tines",
      "favorited": true,
      "creator_id": "12345",
      "created_at": "2025-06-20T08:25:30Z",
      "updated_at": "2025-06-20T08:30:15Z"
    },
    {
      "guid": "b8f2c1a3-9876-4321-a1b2-c3d4e5f6789a",
      "title": "Data analysis discussion",
      "source": "slack",
      "favorited": false,
      "creator_id": "12345",
      "created_at": "2025-06-19T14:20:10Z",
      "updated_at": "2025-06-19T16:45:22Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/workbench?per_page=20&page=1",
    "previous_page": null,
    "next_page": "https://<tenant-domain>/api/v1/workbench?per_page=20&page=2",
    "next_page_number": 2,
    "per_page": 20,
    "pages": 3,
    "count": 42
  }
}
```

### Pagination

This endpoint supports page-based pagination using the `per_page` and `page` parameters:

- Use `per_page` to specify the number of results per page
- Use `page` to specify which page of results to return (defaults to 1)
- The response includes a `meta` object with pagination information including total count and page links

### Filtering

- **search**: Filters conversations containing the search term in their title
- **favorited**: Set to `true` to only return favorited conversations, `false` for non-favorited, or omit for all
- **creator_id**: Only return conversations created by the specified user ID